Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
int
Rust
提交
0a1c5095
R
Rust
项目概览
int
/
Rust
接近 1 年 前同步成功
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
Rust
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
0a1c5095
编写于
9月 11, 2018
作者:
D
Denis Merigoux
提交者:
Eduard-Mihai Burtescu
11月 16, 2018
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Traitified IntrinsicCallMethods
上级
a5aeb8ed
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
648 addition
and
624 deletion
+648
-624
src/librustc_codegen_llvm/base.rs
src/librustc_codegen_llvm/base.rs
+1
-3
src/librustc_codegen_llvm/builder.rs
src/librustc_codegen_llvm/builder.rs
+3
-11
src/librustc_codegen_llvm/context.rs
src/librustc_codegen_llvm/context.rs
+2
-7
src/librustc_codegen_llvm/interfaces/builder.rs
src/librustc_codegen_llvm/interfaces/builder.rs
+12
-4
src/librustc_codegen_llvm/interfaces/intrinsic.rs
src/librustc_codegen_llvm/interfaces/intrinsic.rs
+15
-5
src/librustc_codegen_llvm/interfaces/mod.rs
src/librustc_codegen_llvm/interfaces/mod.rs
+2
-2
src/librustc_codegen_llvm/intrinsic.rs
src/librustc_codegen_llvm/intrinsic.rs
+607
-578
src/librustc_codegen_llvm/mir/block.rs
src/librustc_codegen_llvm/mir/block.rs
+3
-8
src/librustc_codegen_llvm/mir/operand.rs
src/librustc_codegen_llvm/mir/operand.rs
+1
-1
src/librustc_codegen_llvm/mir/place.rs
src/librustc_codegen_llvm/mir/place.rs
+1
-4
src/librustc_codegen_llvm/mir/rvalue.rs
src/librustc_codegen_llvm/mir/rvalue.rs
+1
-1
未找到文件。
src/librustc_codegen_llvm/base.rs
浏览文件 @
0a1c5095
...
...
@@ -73,9 +73,7 @@
use
rustc_data_structures
::
sync
::
Lrc
;
use
rustc_data_structures
::
indexed_vec
::
Idx
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedTypeMethods
,
DerivedIntrinsicMethods
,
};
use
interfaces
::
*
;
use
std
::
any
::
Any
;
use
std
::
cmp
;
...
...
src/librustc_codegen_llvm/builder.rs
浏览文件 @
0a1c5095
...
...
@@ -19,10 +19,7 @@
use
rustc
::
ty
::
layout
::{
Align
,
Size
};
use
rustc
::
session
::{
config
,
Session
};
use
rustc_data_structures
::
small_c_str
::
SmallCStr
;
use
interfaces
::{
Backend
,
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedTypeMethods
,
DerivedIntrinsicMethods
,
};
use
interfaces
::
*
;
use
syntax
;
use
std
::
borrow
::
Cow
;
...
...
@@ -59,16 +56,11 @@ pub struct MemFlags: u8 {
}
}
impl
Backend
for
Builder
<
'a
,
'll
,
'tcx
>
{
type
Value
=
&
'll
Value
;
type
BasicBlock
=
&
'll
BasicBlock
;
type
Type
=
&
'll
Type
;
type
Context
=
&
'll
llvm
::
Context
;
impl
HasCodegen
for
Builder
<
'a
,
'll
,
'tcx
>
{
type
CodegenCx
=
CodegenCx
<
'll
,
'tcx
>
;
}
impl
BuilderMethods
<
'a
,
'tcx
>
for
Builder
<
'a
,
'll
,
'tcx
>
{
type
CodegenCx
=
CodegenCx
<
'll
,
'tcx
>
;
fn
new_block
<
'b
>
(
cx
:
&
'a
CodegenCx
<
'll
,
'tcx
>
,
llfn
:
&
'll
Value
,
...
...
src/librustc_codegen_llvm/context.rs
浏览文件 @
0a1c5095
...
...
@@ -23,8 +23,7 @@
use
monomorphize
::
partitioning
::
CodegenUnit
;
use
type_
::
Type
;
use
type_of
::
PointeeInfo
;
use
interfaces
::{
BaseTypeMethods
,
DerivedTypeMethods
,
IntrinsicMethods
,
BaseIntrinsicMethods
,
DerivedIntrinsicMethods
};
use
interfaces
::{
BaseTypeMethods
,
DerivedTypeMethods
,
IntrinsicDeclarationMethods
};
use
rustc_data_structures
::
base_n
;
use
rustc_data_structures
::
small_c_str
::
SmallCStr
;
...
...
@@ -323,9 +322,7 @@ pub fn sess<'a>(&'a self) -> &'a Session {
}
}
impl
BaseIntrinsicMethods
for
CodegenCx
<
'_
,
'_
>
{}
impl
DerivedIntrinsicMethods
for
CodegenCx
<
'b
,
'tcx
>
{
impl
IntrinsicDeclarationMethods
for
CodegenCx
<
'b
,
'tcx
>
{
fn
get_intrinsic
(
&
self
,
key
:
&
str
)
->
&
'b
Value
{
if
let
Some
(
v
)
=
self
.intrinsics
.borrow
()
.get
(
key
)
.cloned
()
{
return
v
;
...
...
@@ -647,8 +644,6 @@ fn declare_intrinsic(
}
}
impl
IntrinsicMethods
for
CodegenCx
<
'a
,
'tcx
>
{}
impl
<
'b
,
'tcx
>
CodegenCx
<
'b
,
'tcx
>
{
/// Generate a new symbol name with the given prefix. This symbol name must
/// only be used for definitions with `internal` or `private` linkage.
...
...
src/librustc_codegen_llvm/interfaces/builder.rs
浏览文件 @
0a1c5095
...
...
@@ -17,21 +17,29 @@
use
super
::
backend
::
Backend
;
use
super
::
type_
::
TypeMethods
;
use
super
::
consts
::
ConstMethods
;
use
super
::
intrinsic
::
IntrinsicMethods
;
use
super
::
intrinsic
::
Intrinsic
Declaration
Methods
;
use
std
::
borrow
::
Cow
;
use
std
::
ops
::
Range
;
use
syntax
::
ast
::
AsmDialect
;
pub
trait
BuilderMethods
<
'a
,
'tcx
:
'a
>
:
Backend
{
type
CodegenCx
:
'a
+
TypeMethods
+
ConstMethods
+
IntrinsicMethods
+
Backend
<
pub
trait
HasCodegen
:
Backend
{
type
CodegenCx
:
TypeMethods
+
ConstMethods
+
IntrinsicDeclarationMethods
+
Backend
<
Value
=
Self
::
Value
,
BasicBlock
=
Self
::
BasicBlock
,
Type
=
Self
::
Type
,
Context
=
Self
::
Context
,
>
;
}
impl
<
T
:
HasCodegen
>
Backend
for
T
{
type
Value
=
<
T
::
CodegenCx
as
Backend
>
::
Value
;
type
BasicBlock
=
<
T
::
CodegenCx
as
Backend
>
::
BasicBlock
;
type
Type
=
<
T
::
CodegenCx
as
Backend
>
::
Type
;
type
Context
=
<
T
::
CodegenCx
as
Backend
>
::
Context
;
}
pub
trait
BuilderMethods
<
'a
,
'tcx
:
'a
>
:
HasCodegen
{
fn
new_block
<
'b
>
(
cx
:
&
'a
Self
::
CodegenCx
,
llfn
:
Self
::
Value
,
...
...
src/librustc_codegen_llvm/interfaces/intrinsic.rs
浏览文件 @
0a1c5095
...
...
@@ -9,17 +9,27 @@
// except according to those terms.
use
super
::
backend
::
Backend
;
use
super
::
builder
::
HasCodegen
;
use
mir
::
operand
::
OperandRef
;
use
rustc
::
ty
::
Ty
;
use
abi
::
FnType
;
use
syntax_pos
::
Span
;
pub
trait
BaseIntrinsicMethods
:
Backend
{
pub
trait
IntrinsicCallMethods
<
'a
,
'tcx
:
'a
>
:
HasCodegen
{
fn
codegen_intrinsic_call
(
&
self
,
callee_ty
:
Ty
<
'tcx
>
,
fn_ty
:
&
FnType
<
'tcx
,
Ty
<
'tcx
>>
,
args
:
&
[
OperandRef
<
'tcx
,
Self
::
Value
>
],
llresult
:
Self
::
Value
,
span
:
Span
,
);
}
pub
trait
DerivedIntrinsic
Methods
:
Backend
{
pub
trait
IntrinsicDeclaration
Methods
:
Backend
{
fn
get_intrinsic
(
&
self
,
key
:
&
str
)
->
Self
::
Value
;
fn
declare_intrinsic
(
&
self
,
key
:
&
str
)
->
Option
<
Self
::
Value
>
;
}
pub
trait
IntrinsicMethods
:
BaseIntrinsicMethods
+
DerivedIntrinsicMethods
{}
src/librustc_codegen_llvm/interfaces/mod.rs
浏览文件 @
0a1c5095
...
...
@@ -15,9 +15,9 @@
mod
intrinsic
;
mod
statics
;
pub
use
self
::
builder
::
BuilderMethods
;
pub
use
self
::
builder
::
{
BuilderMethods
,
HasCodegen
}
;
pub
use
self
::
backend
::
Backend
;
pub
use
self
::
consts
::
ConstMethods
;
pub
use
self
::
type_
::{
TypeMethods
,
BaseTypeMethods
,
DerivedTypeMethods
};
pub
use
self
::
intrinsic
::{
Intrinsic
Methods
,
BaseIntrinsicMethods
,
DerivedIntrinsic
Methods
};
pub
use
self
::
intrinsic
::{
Intrinsic
CallMethods
,
IntrinsicDeclaration
Methods
};
pub
use
self
::
statics
::
StaticMethods
;
src/librustc_codegen_llvm/intrinsic.rs
浏览文件 @
0a1c5095
...
...
@@ -32,10 +32,7 @@
use
builder
::{
Builder
,
MemFlags
};
use
value
::
Value
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedTypeMethods
,
DerivedIntrinsicMethods
,
StaticMethods
,
};
use
interfaces
::
*
;
use
rustc
::
session
::
Session
;
use
syntax_pos
::
Span
;
...
...
@@ -90,635 +87,667 @@ fn get_simple_intrinsic(cx: &CodegenCx<'ll, '_>, name: &str) -> Option<&'ll Valu
Some
(
cx
.get_intrinsic
(
&
llvm_name
))
}
/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
/// add them to librustc_codegen_llvm/context.rs
pub
fn
codegen_intrinsic_call
(
bx
:
&
Builder
<
'a
,
'll
,
'tcx
>
,
callee_ty
:
Ty
<
'tcx
>
,
fn_ty
:
&
FnType
<
'tcx
,
Ty
<
'tcx
>>
,
args
:
&
[
OperandRef
<
'tcx
,
&
'll
Value
>
],
llresult
:
&
'll
Value
,
span
:
Span
,
)
{
let
cx
=
bx
.cx
();
let
tcx
=
cx
.tcx
;
impl
IntrinsicCallMethods
<
'a
,
'tcx
>
for
Builder
<
'a
,
'll
,
'tcx
>
{
/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs,
/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics,
/// add them to librustc_codegen_llvm/context.rs
fn
codegen_intrinsic_call
(
&
self
,
callee_ty
:
Ty
<
'tcx
>
,
fn_ty
:
&
FnType
<
'tcx
,
Ty
<
'tcx
>>
,
args
:
&
[
OperandRef
<
'tcx
,
&
'll
Value
>
],
llresult
:
&
'll
Value
,
span
:
Span
,
)
{
let
cx
=
self
.cx
();
let
tcx
=
cx
.tcx
;
let
(
def_id
,
substs
)
=
match
callee_ty
.sty
{
ty
::
FnDef
(
def_id
,
substs
)
=>
(
def_id
,
substs
),
_
=>
bug!
(
"expected fn item type, found {}"
,
callee_ty
)
};
let
(
def_id
,
substs
)
=
match
callee_ty
.sty
{
ty
::
FnDef
(
def_id
,
substs
)
=>
(
def_id
,
substs
),
_
=>
bug!
(
"expected fn item type, found {}"
,
callee_ty
)
};
let
sig
=
callee_ty
.fn_sig
(
tcx
);
let
sig
=
tcx
.normalize_erasing_late_bound_regions
(
ty
::
ParamEnv
::
reveal_all
(),
&
sig
);
let
arg_tys
=
sig
.inputs
();
let
ret_ty
=
sig
.output
();
let
name
=
&*
tcx
.item_name
(
def_id
)
.as_str
();
let
llret_ty
=
cx
.layout_of
(
ret_ty
)
.llvm_type
(
cx
);
let
result
=
PlaceRef
::
new_sized
(
llresult
,
fn_ty
.ret.layout
,
fn_ty
.ret.layout.align
);
let
simple
=
get_simple_intrinsic
(
cx
,
name
);
let
llval
=
match
name
{
_
if
simple
.is_some
()
=>
{
bx
.call
(
simple
.unwrap
(),
&
args
.iter
()
.map
(|
arg
|
arg
.immediate
())
.collect
::
<
Vec
<
_
>>
(),
None
)
}
"unreachable"
=>
{
return
;
},
"likely"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.expect.i1"
));
bx
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
bx
.cx
()
.const_bool
(
true
)],
None
)
}
"unlikely"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.expect.i1"
));
bx
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
bx
.cx
()
.const_bool
(
false
)],
None
)
}
"try"
=>
{
try_intrinsic
(
bx
,
cx
,
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
(),
llresult
);
return
;
}
"breakpoint"
=>
{
let
llfn
=
cx
.get_intrinsic
(
&
(
"llvm.debugtrap"
));
bx
.call
(
llfn
,
&
[],
None
)
}
"size_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.size_of
(
tp_ty
)
.bytes
())
}
"size_of_val"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
if
let
OperandValue
::
Pair
(
_
,
meta
)
=
args
[
0
]
.val
{
let
(
llsize
,
_
)
=
glue
::
size_and_align_of_dst
(
bx
,
tp_ty
,
Some
(
meta
));
llsize
}
else
{
let
sig
=
callee_ty
.fn_sig
(
tcx
);
let
sig
=
tcx
.normalize_erasing_late_bound_regions
(
ty
::
ParamEnv
::
reveal_all
(),
&
sig
);
let
arg_tys
=
sig
.inputs
();
let
ret_ty
=
sig
.output
();
let
name
=
&*
tcx
.item_name
(
def_id
)
.as_str
();
let
llret_ty
=
cx
.layout_of
(
ret_ty
)
.llvm_type
(
cx
);
let
result
=
PlaceRef
::
new_sized
(
llresult
,
fn_ty
.ret.layout
,
fn_ty
.ret.layout.align
);
let
simple
=
get_simple_intrinsic
(
cx
,
name
);
let
llval
=
match
name
{
_
if
simple
.is_some
()
=>
{
self
.call
(
simple
.unwrap
(),
&
args
.iter
()
.map
(|
arg
|
arg
.immediate
())
.collect
::
<
Vec
<
_
>>
(),
None
)
}
"unreachable"
=>
{
return
;
},
"likely"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.expect.i1"
));
self
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
cx
.const_bool
(
true
)],
None
)
}
"unlikely"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.expect.i1"
));
self
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
cx
.const_bool
(
false
)],
None
)
}
"try"
=>
{
try_intrinsic
(
self
,
cx
,
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
(),
llresult
);
return
;
}
"breakpoint"
=>
{
let
llfn
=
cx
.get_intrinsic
(
&
(
"llvm.debugtrap"
));
self
.call
(
llfn
,
&
[],
None
)
}
"size_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.size_of
(
tp_ty
)
.bytes
())
}
}
"min_align_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.align_of
(
tp_ty
)
.abi
())
}
"min_align_of_val"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
if
let
OperandValue
::
Pair
(
_
,
meta
)
=
args
[
0
]
.val
{
let
(
_
,
llalign
)
=
glue
::
size_and_align_of_dst
(
bx
,
tp_ty
,
Some
(
meta
));
llalign
}
else
{
"size_of_val"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
if
let
OperandValue
::
Pair
(
_
,
meta
)
=
args
[
0
]
.val
{
let
(
llsize
,
_
)
=
glue
::
size_and_align_of_dst
(
&
self
,
tp_ty
,
Some
(
meta
));
llsize
}
else
{
cx
.const_usize
(
cx
.size_of
(
tp_ty
)
.bytes
())
}
}
"min_align_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.align_of
(
tp_ty
)
.abi
())
}
}
"pref_align_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.align_of
(
tp_ty
)
.pref
())
}
"type_name"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
let
ty_name
=
Symbol
::
intern
(
&
tp_ty
.to_string
())
.as_str
();
cx
.const_str_slice
(
ty_name
)
}
"type_id"
=>
{
cx
.const_u64
(
cx
.tcx
.type_id_hash
(
substs
.type_at
(
0
)))
}
"init"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
!
cx
.layout_of
(
ty
)
.is_zst
()
{
// Just zero out the stack slot.
// If we 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.)
memset_intrinsic
(
bx
,
false
,
ty
,
llresult
,
cx
.const_u8
(
0
),
cx
.const_usize
(
1
)
);
"min_align_of_val"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
if
let
OperandValue
::
Pair
(
_
,
meta
)
=
args
[
0
]
.val
{
let
(
_
,
llalign
)
=
glue
::
size_and_align_of_dst
(
&
self
,
tp_ty
,
Some
(
meta
));
llalign
}
else
{
cx
.const_usize
(
cx
.align_of
(
tp_ty
)
.abi
())
}
}
return
;
}
// Effectively no-ops
"uninit"
|
"forget"
=>
{
return
;
}
"needs_drop"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
"pref_align_of"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_usize
(
cx
.align_of
(
tp_ty
)
.pref
())
}
"type_name"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
let
ty_name
=
Symbol
::
intern
(
&
tp_ty
.to_string
())
.as_str
();
cx
.const_str_slice
(
ty_name
)
}
"type_id"
=>
{
cx
.const_u64
(
cx
.tcx
.type_id_hash
(
substs
.type_at
(
0
)))
}
"init"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
!
cx
.layout_of
(
ty
)
.is_zst
()
{
// Just zero out the stack slot.
// If we 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.)
memset_intrinsic
(
&
self
,
false
,
ty
,
llresult
,
cx
.const_u8
(
0
),
cx
.const_usize
(
1
)
);
}
return
;
}
// Effectively no-ops
"uninit"
|
"forget"
=>
{
return
;
}
"needs_drop"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
cx
.const_bool
(
bx
.cx
()
.type_needs_drop
(
tp_ty
))
}
"offset"
=>
{
let
ptr
=
args
[
0
]
.immediate
();
let
offset
=
args
[
1
]
.immediate
();
bx
.inbounds_gep
(
ptr
,
&
[
offset
])
}
"arith_offset"
=>
{
let
ptr
=
args
[
0
]
.immediate
();
let
offset
=
args
[
1
]
.immediate
();
bx
.gep
(
ptr
,
&
[
offset
])
}
cx
.const_bool
(
cx
.type_needs_drop
(
tp_ty
))
}
"offset"
=>
{
let
ptr
=
args
[
0
]
.immediate
();
let
offset
=
args
[
1
]
.immediate
();
self
.inbounds_gep
(
ptr
,
&
[
offset
])
}
"arith_offset"
=>
{
let
ptr
=
args
[
0
]
.immediate
();
let
offset
=
args
[
1
]
.immediate
();
self
.gep
(
ptr
,
&
[
offset
])
}
"copy_nonoverlapping"
=>
{
copy_intrinsic
(
bx
,
false
,
false
,
substs
.type_at
(
0
),
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"copy"
=>
{
copy_intrinsic
(
bx
,
true
,
false
,
substs
.type_at
(
0
),
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"write_bytes"
=>
{
memset_intrinsic
(
bx
,
false
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"copy_nonoverlapping"
=>
{
copy_intrinsic
(
&
self
,
false
,
false
,
substs
.type_at
(
0
),
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"copy"
=>
{
copy_intrinsic
(
&
self
,
true
,
false
,
substs
.type_at
(
0
),
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"write_bytes"
=>
{
memset_intrinsic
(
&
self
,
false
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"volatile_copy_nonoverlapping_memory"
=>
{
copy_intrinsic
(
bx
,
false
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"volatile_copy_memory"
=>
{
copy_intrinsic
(
bx
,
true
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"volatile_set_memory"
=>
{
memset_intrinsic
(
bx
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"volatile_load"
|
"unaligned_volatile_load"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
let
mut
ptr
=
args
[
0
]
.immediate
();
if
let
PassMode
::
Cast
(
ty
)
=
fn_ty
.ret.mode
{
ptr
=
bx
.pointercast
(
ptr
,
bx
.cx
()
.type_ptr_to
(
ty
.llvm_type
(
cx
)));
"volatile_copy_nonoverlapping_memory"
=>
{
copy_intrinsic
(
&
self
,
false
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
let
load
=
bx
.volatile_load
(
ptr
);
let
align
=
if
name
==
"unaligned_volatile_load"
{
1
}
else
{
cx
.align_of
(
tp_ty
)
.abi
()
as
u32
};
unsafe
{
llvm
::
LLVMSetAlignment
(
load
,
align
);
"volatile_copy_memory"
=>
{
copy_intrinsic
(
&
self
,
true
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
to_immediate
(
bx
,
load
,
cx
.layout_of
(
tp_ty
))
},
"volatile_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
bx
.cx
());
args
[
1
]
.val
.volatile_store
(
bx
,
dst
);
return
;
},
"unaligned_volatile_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
bx
.cx
());
args
[
1
]
.val
.unaligned_volatile_store
(
bx
,
dst
);
return
;
},
"prefetch_read_data"
|
"prefetch_write_data"
|
"prefetch_read_instruction"
|
"prefetch_write_instruction"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.prefetch"
));
let
(
rw
,
cache_type
)
=
match
name
{
"prefetch_read_data"
=>
(
0
,
1
),
"prefetch_write_data"
=>
(
1
,
1
),
"prefetch_read_instruction"
=>
(
0
,
0
),
"prefetch_write_instruction"
=>
(
1
,
0
),
_
=>
bug!
()
};
bx
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
cx
.const_i32
(
rw
),
args
[
1
]
.immediate
(),
cx
.const_i32
(
cache_type
)
],
None
)
},
"ctlz"
|
"ctlz_nonzero"
|
"cttz"
|
"cttz_nonzero"
|
"ctpop"
|
"bswap"
|
"bitreverse"
|
"add_with_overflow"
|
"sub_with_overflow"
|
"mul_with_overflow"
|
"overflowing_add"
|
"overflowing_sub"
|
"overflowing_mul"
|
"unchecked_div"
|
"unchecked_rem"
|
"unchecked_shl"
|
"unchecked_shr"
|
"exact_div"
|
"rotate_left"
|
"rotate_right"
=>
{
let
ty
=
arg_tys
[
0
];
match
int_type_width_signed
(
ty
,
cx
)
{
Some
((
width
,
signed
))
=>
match
name
{
"ctlz"
|
"cttz"
=>
{
let
y
=
cx
.const_bool
(
false
);
let
llfn
=
cx
.get_intrinsic
(
&
format!
(
"llvm.{}.i{}"
,
name
,
width
));
bx
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
y
],
None
)
}
"ctlz_nonzero"
|
"cttz_nonzero"
=>
{
let
y
=
cx
.const_bool
(
true
);
let
llvm_name
=
&
format!
(
"llvm.{}.i{}"
,
&
name
[
..
4
],
width
);
let
llfn
=
cx
.get_intrinsic
(
llvm_name
);
bx
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
y
],
None
)
}
"ctpop"
=>
bx
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.ctpop.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
),
"bswap"
=>
{
if
width
==
8
{
args
[
0
]
.immediate
()
// byte swap a u8/i8 is just a no-op
}
else
{
bx
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.bswap.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
)
"volatile_set_memory"
=>
{
memset_intrinsic
(
&
self
,
true
,
substs
.type_at
(
0
),
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
());
return
;
}
"volatile_load"
|
"unaligned_volatile_load"
=>
{
let
tp_ty
=
substs
.type_at
(
0
);
let
mut
ptr
=
args
[
0
]
.immediate
();
if
let
PassMode
::
Cast
(
ty
)
=
fn_ty
.ret.mode
{
ptr
=
self
.pointercast
(
ptr
,
cx
.type_ptr_to
(
ty
.llvm_type
(
cx
)));
}
let
load
=
self
.volatile_load
(
ptr
);
let
align
=
if
name
==
"unaligned_volatile_load"
{
1
}
else
{
cx
.align_of
(
tp_ty
)
.abi
()
as
u32
};
unsafe
{
llvm
::
LLVMSetAlignment
(
load
,
align
);
}
to_immediate
(
self
,
load
,
cx
.layout_of
(
tp_ty
))
},
"volatile_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
cx
);
args
[
1
]
.val
.volatile_store
(
&
self
,
dst
);
return
;
},
"unaligned_volatile_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
cx
);
args
[
1
]
.val
.unaligned_volatile_store
(
&
self
,
dst
);
return
;
},
"prefetch_read_data"
|
"prefetch_write_data"
|
"prefetch_read_instruction"
|
"prefetch_write_instruction"
=>
{
let
expect
=
cx
.get_intrinsic
(
&
(
"llvm.prefetch"
));
let
(
rw
,
cache_type
)
=
match
name
{
"prefetch_read_data"
=>
(
0
,
1
),
"prefetch_write_data"
=>
(
1
,
1
),
"prefetch_read_instruction"
=>
(
0
,
0
),
"prefetch_write_instruction"
=>
(
1
,
0
),
_
=>
bug!
()
};
self
.call
(
expect
,
&
[
args
[
0
]
.immediate
(),
cx
.const_i32
(
rw
),
args
[
1
]
.immediate
(),
cx
.const_i32
(
cache_type
)
],
None
)
},
"ctlz"
|
"ctlz_nonzero"
|
"cttz"
|
"cttz_nonzero"
|
"ctpop"
|
"bswap"
|
"bitreverse"
|
"add_with_overflow"
|
"sub_with_overflow"
|
"mul_with_overflow"
|
"overflowing_add"
|
"overflowing_sub"
|
"overflowing_mul"
|
"unchecked_div"
|
"unchecked_rem"
|
"unchecked_shl"
|
"unchecked_shr"
|
"exact_div"
|
"rotate_left"
|
"rotate_right"
=>
{
let
ty
=
arg_tys
[
0
];
match
int_type_width_signed
(
ty
,
cx
)
{
Some
((
width
,
signed
))
=>
match
name
{
"ctlz"
|
"cttz"
=>
{
let
y
=
cx
.const_bool
(
false
);
let
llfn
=
cx
.get_intrinsic
(
&
format!
(
"llvm.{}.i{}"
,
name
,
width
));
self
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
y
],
None
)
}
}
"bitreverse"
=>
{
bx
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.bitreverse.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
)
}
"add_with_overflow"
|
"sub_with_overflow"
|
"mul_with_overflow"
=>
{
let
intrinsic
=
format!
(
"llvm.{}{}.with.overflow.i{}"
,
if
signed
{
's'
}
else
{
'u'
},
&
name
[
..
3
],
width
);
let
llfn
=
bx
.cx
()
.get_intrinsic
(
&
intrinsic
);
// Convert `i1` to a `bool`, and write it to the out parameter
let
pair
=
bx
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()
],
None
);
let
val
=
bx
.extract_value
(
pair
,
0
);
let
overflow
=
bx
.zext
(
bx
.extract_value
(
pair
,
1
),
cx
.type_bool
());
let
dest
=
result
.project_field
(
bx
,
0
);
bx
.store
(
val
,
dest
.llval
,
dest
.align
);
let
dest
=
result
.project_field
(
bx
,
1
);
bx
.store
(
overflow
,
dest
.llval
,
dest
.align
);
return
;
},
"overflowing_add"
=>
bx
.add
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"overflowing_sub"
=>
bx
.sub
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"overflowing_mul"
=>
bx
.mul
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"exact_div"
=>
if
signed
{
bx
.exactsdiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
bx
.exactudiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_div"
=>
if
signed
{
bx
.sdiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
bx
.udiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_rem"
=>
if
signed
{
bx
.srem
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
bx
.urem
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_shl"
=>
bx
.shl
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"unchecked_shr"
=>
if
signed
{
bx
.ashr
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
bx
.lshr
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"rotate_left"
|
"rotate_right"
=>
{
let
is_left
=
name
==
"rotate_left"
;
let
val
=
args
[
0
]
.immediate
();
let
raw_shift
=
args
[
1
]
.immediate
();
if
llvm_util
::
get_major_version
()
>=
7
{
// rotate = funnel shift with first two args the same
let
llvm_name
=
&
format!
(
"llvm.fsh{}.i{}"
,
if
is_left
{
'l'
}
else
{
'r'
},
width
);
"ctlz_nonzero"
|
"cttz_nonzero"
=>
{
let
y
=
cx
.const_bool
(
true
);
let
llvm_name
=
&
format!
(
"llvm.{}.i{}"
,
&
name
[
..
4
],
width
);
let
llfn
=
cx
.get_intrinsic
(
llvm_name
);
bx
.call
(
llfn
,
&
[
val
,
val
,
raw_shift
],
None
)
}
else
{
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
let
width
=
cx
.const_uint
(
cx
.type_ix
(
width
),
width
);
let
shift
=
bx
.urem
(
raw_shift
,
width
);
let
inv_shift
=
bx
.urem
(
bx
.sub
(
width
,
raw_shift
),
width
);
let
shift1
=
bx
.shl
(
val
,
if
is_left
{
shift
}
else
{
inv_shift
});
let
shift2
=
bx
.lshr
(
val
,
if
!
is_left
{
shift
}
else
{
inv_shift
});
bx
.or
(
shift1
,
shift2
)
self
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
y
],
None
)
}
"ctpop"
=>
self
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.ctpop.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
),
"bswap"
=>
{
if
width
==
8
{
args
[
0
]
.immediate
()
// byte swap a u8/i8 is just a no-op
}
else
{
self
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.bswap.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
)
}
}
"bitreverse"
=>
{
self
.call
(
cx
.get_intrinsic
(
&
format!
(
"llvm.bitreverse.i{}"
,
width
)),
&
[
args
[
0
]
.immediate
()],
None
)
}
"add_with_overflow"
|
"sub_with_overflow"
|
"mul_with_overflow"
=>
{
let
intrinsic
=
format!
(
"llvm.{}{}.with.overflow.i{}"
,
if
signed
{
's'
}
else
{
'u'
},
&
name
[
..
3
],
width
);
let
llfn
=
cx
.get_intrinsic
(
&
intrinsic
);
// Convert `i1` to a `bool`, and write it to the out parameter
let
pair
=
self
.call
(
llfn
,
&
[
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()
],
None
);
let
val
=
self
.extract_value
(
pair
,
0
);
let
overflow
=
self
.zext
(
self
.extract_value
(
pair
,
1
),
cx
.type_bool
()
);
let
dest
=
result
.project_field
(
&
self
,
0
);
self
.store
(
val
,
dest
.llval
,
dest
.align
);
let
dest
=
result
.project_field
(
&
self
,
1
);
self
.store
(
overflow
,
dest
.llval
,
dest
.align
);
return
;
},
"overflowing_add"
=>
self
.add
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"overflowing_sub"
=>
self
.sub
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"overflowing_mul"
=>
self
.mul
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"exact_div"
=>
if
signed
{
self
.exactsdiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
self
.exactudiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_div"
=>
if
signed
{
self
.sdiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
self
.udiv
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_rem"
=>
if
signed
{
self
.srem
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
self
.urem
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"unchecked_shl"
=>
self
.shl
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"unchecked_shr"
=>
if
signed
{
self
.ashr
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
}
else
{
self
.lshr
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
())
},
"rotate_left"
|
"rotate_right"
=>
{
let
is_left
=
name
==
"rotate_left"
;
let
val
=
args
[
0
]
.immediate
();
let
raw_shift
=
args
[
1
]
.immediate
();
if
llvm_util
::
get_major_version
()
>=
7
{
// rotate = funnel shift with first two args the same
let
llvm_name
=
&
format!
(
"llvm.fsh{}.i{}"
,
if
is_left
{
'l'
}
else
{
'r'
},
width
);
let
llfn
=
cx
.get_intrinsic
(
llvm_name
);
self
.call
(
llfn
,
&
[
val
,
val
,
raw_shift
],
None
)
}
else
{
// rotate_left: (X << (S % BW)) | (X >> ((BW - S) % BW))
// rotate_right: (X << ((BW - S) % BW)) | (X >> (S % BW))
let
width
=
cx
.const_uint
(
cx
.type_ix
(
width
),
width
);
let
shift
=
self
.urem
(
raw_shift
,
width
);
let
inv_shift
=
self
.urem
(
self
.sub
(
width
,
raw_shift
),
width
);
let
shift1
=
self
.shl
(
val
,
if
is_left
{
shift
}
else
{
inv_shift
},
);
let
shift2
=
self
.lshr
(
val
,
if
!
is_left
{
shift
}
else
{
inv_shift
},
);
self
.or
(
shift1
,
shift2
)
}
},
_
=>
bug!
(),
},
_
=>
bug!
(),
},
None
=>
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic integer type, found `{}`"
,
name
,
ty
));
return
;
None
=>
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic integer type, found `{}`"
,
name
,
ty
));
return
;
}
}
}
},
"fadd_fast"
|
"fsub_fast"
|
"fmul_fast"
|
"fdiv_fast"
|
"frem_fast"
=>
{
let
sty
=
&
arg_tys
[
0
]
.sty
;
match
float_type_width
(
sty
)
{
Some
(
_
width
)
=>
match
name
{
"fadd_fast"
=>
bx
.fadd_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fsub_fast"
=>
bx
.fsub_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fmul_fast"
=>
bx
.fmul_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fdiv_fast"
=>
bx
.fdiv_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"frem_fast"
=>
bx
.frem_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
_
=>
bug!
(),
},
None
=>
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic float type, found `{}`"
,
name
,
sty
));
return
;
},
"fadd_fast"
|
"fsub_fast"
|
"fmul_fast"
|
"fdiv_fast"
|
"frem_fast"
=>
{
let
sty
=
&
arg_tys
[
0
]
.sty
;
match
float_type_width
(
sty
)
{
Some
(
_
width
)
=>
match
name
{
"fadd_fast"
=>
self
.fadd_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fsub_fast"
=>
self
.fsub_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fmul_fast"
=>
self
.fmul_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"fdiv_fast"
=>
self
.fdiv_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
"frem_fast"
=>
self
.frem_fast
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
()),
_
=>
bug!
(),
},
None
=>
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic float type, found `{}`"
,
name
,
sty
));
return
;
}
}
}
},
},
"discriminant_value"
=>
{
args
[
0
]
.deref
(
bx
.cx
())
.codegen_get_discr
(
bx
,
ret_ty
)
}
"discriminant_value"
=>
{
args
[
0
]
.deref
(
cx
)
.codegen_get_discr
(
&
self
,
ret_ty
)
}
name
if
name
.starts_with
(
"simd_"
)
=>
{
match
generic_simd_intrinsic
(
bx
,
name
,
callee_ty
,
args
,
ret_ty
,
llret_ty
,
span
)
{
Ok
(
llval
)
=>
llval
,
Err
(())
=>
return
name
if
name
.starts_with
(
"simd_"
)
=>
{
match
generic_simd_intrinsic
(
&
self
,
name
,
callee_ty
,
args
,
ret_ty
,
llret_ty
,
span
)
{
Ok
(
llval
)
=>
llval
,
Err
(())
=>
return
}
}
}
// This requires that atomic intrinsics follow a specific naming pattern:
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
name
if
name
.starts_with
(
"atomic_"
)
=>
{
use
self
::
AtomicOrdering
::
*
;
let
split
:
Vec
<&
str
>
=
name
.split
(
'_'
)
.collect
();
let
is_cxchg
=
split
[
1
]
==
"cxchg"
||
split
[
1
]
==
"cxchgweak"
;
let
(
order
,
failorder
)
=
match
split
.len
()
{
2
=>
(
SequentiallyConsistent
,
SequentiallyConsistent
),
3
=>
match
split
[
2
]
{
"unordered"
=>
(
Unordered
,
Unordered
),
"relaxed"
=>
(
Monotonic
,
Monotonic
),
"acq"
=>
(
Acquire
,
Acquire
),
"rel"
=>
(
Release
,
Monotonic
),
"acqrel"
=>
(
AcquireRelease
,
Acquire
),
"failrelaxed"
if
is_cxchg
=>
(
SequentiallyConsistent
,
Monotonic
),
"failacq"
if
is_cxchg
=>
(
SequentiallyConsistent
,
Acquire
),
_
=>
cx
.sess
()
.fatal
(
"unknown ordering in atomic intrinsic"
)
},
4
=>
match
(
split
[
2
],
split
[
3
])
{
(
"acq"
,
"failrelaxed"
)
if
is_cxchg
=>
(
Acquire
,
Monotonic
),
(
"acqrel"
,
"failrelaxed"
)
if
is_cxchg
=>
(
AcquireRelease
,
Monotonic
),
_
=>
cx
.sess
()
.fatal
(
"unknown ordering in atomic intrinsic"
)
},
_
=>
cx
.sess
()
.fatal
(
"Atomic intrinsic not in correct format"
),
};
// This requires that atomic intrinsics follow a specific naming pattern:
// "atomic_<operation>[_<ordering>]", and no ordering means SeqCst
name
if
name
.starts_with
(
"atomic_"
)
=>
{
use
self
::
AtomicOrdering
::
*
;
let
split
:
Vec
<&
str
>
=
name
.split
(
'_'
)
.collect
();
let
is_cxchg
=
split
[
1
]
==
"cxchg"
||
split
[
1
]
==
"cxchgweak"
;
let
(
order
,
failorder
)
=
match
split
.len
()
{
2
=>
(
SequentiallyConsistent
,
SequentiallyConsistent
),
3
=>
match
split
[
2
]
{
"unordered"
=>
(
Unordered
,
Unordered
),
"relaxed"
=>
(
Monotonic
,
Monotonic
),
"acq"
=>
(
Acquire
,
Acquire
),
"rel"
=>
(
Release
,
Monotonic
),
"acqrel"
=>
(
AcquireRelease
,
Acquire
),
"failrelaxed"
if
is_cxchg
=>
(
SequentiallyConsistent
,
Monotonic
),
"failacq"
if
is_cxchg
=>
(
SequentiallyConsistent
,
Acquire
),
_
=>
cx
.sess
()
.fatal
(
"unknown ordering in atomic intrinsic"
)
},
4
=>
match
(
split
[
2
],
split
[
3
])
{
(
"acq"
,
"failrelaxed"
)
if
is_cxchg
=>
(
Acquire
,
Monotonic
),
(
"acqrel"
,
"failrelaxed"
)
if
is_cxchg
=>
(
AcquireRelease
,
Monotonic
),
_
=>
cx
.sess
()
.fatal
(
"unknown ordering in atomic intrinsic"
)
},
_
=>
cx
.sess
()
.fatal
(
"Atomic intrinsic not in correct format"
),
};
let
invalid_monomorphization
=
|
ty
|
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic integer type, found `{}`"
,
name
,
ty
));
};
let
invalid_monomorphization
=
|
ty
|
{
span_invalid_monomorphization_error
(
tcx
.sess
,
span
,
&
format!
(
"invalid monomorphization of `{}` intrinsic:
\
expected basic integer type, found `{}`"
,
name
,
ty
));
};
match
split
[
1
]
{
"cxchg"
|
"cxchgweak"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
weak
=
split
[
1
]
==
"cxchgweak"
;
let
pair
=
bx
.atomic_cmpxchg
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
(),
order
,
failorder
,
weak
);
let
val
=
bx
.extract_value
(
pair
,
0
);
let
success
=
bx
.zext
(
bx
.extract_value
(
pair
,
1
),
bx
.cx
()
.type_bool
());
let
dest
=
result
.project_field
(
bx
,
0
);
bx
.store
(
val
,
dest
.llval
,
dest
.align
);
let
dest
=
result
.project_field
(
bx
,
1
);
bx
.store
(
success
,
dest
.llval
,
dest
.align
);
return
;
}
else
{
return
invalid_monomorphization
(
ty
);
match
split
[
1
]
{
"cxchg"
|
"cxchgweak"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
weak
=
split
[
1
]
==
"cxchgweak"
;
let
pair
=
self
.atomic_cmpxchg
(
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
args
[
2
]
.immediate
(),
order
,
failorder
,
weak
);
let
val
=
self
.extract_value
(
pair
,
0
);
let
success
=
self
.zext
(
self
.extract_value
(
pair
,
1
),
cx
.type_bool
()
);
let
dest
=
result
.project_field
(
&
self
,
0
);
self
.store
(
val
,
dest
.llval
,
dest
.align
);
let
dest
=
result
.project_field
(
&
self
,
1
);
self
.store
(
success
,
dest
.llval
,
dest
.align
);
return
;
}
else
{
return
invalid_monomorphization
(
ty
);
}
}
}
"load"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
size
=
cx
.size_of
(
ty
);
bx
.atomic_load
(
args
[
0
]
.immediate
(),
order
,
size
)
}
else
{
return
invalid_monomorphization
(
ty
);
"load"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
size
=
cx
.size_of
(
ty
);
self
.atomic_load
(
args
[
0
]
.immediate
(),
order
,
size
)
}
else
{
return
invalid_monomorphization
(
ty
);
}
}
"store"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
size
=
cx
.size_of
(
ty
);
self
.atomic_store
(
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
order
,
size
);
return
;
}
else
{
return
invalid_monomorphization
(
ty
);
}
}
}
"store"
=>
{
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
let
size
=
cx
.size_of
(
ty
);
bx
.atomic_store
(
args
[
1
]
.immediate
(),
args
[
0
]
.immediate
(),
order
,
size
);
"fence"
=>
{
self
.atomic_fence
(
order
,
SynchronizationScope
::
CrossThread
);
return
;
}
else
{
return
invalid_monomorphization
(
ty
);
}
}
"
fence"
=>
{
bx
.atomic_fence
(
order
,
SynchronizationScope
::
Cross
Thread
);
return
;
}
"singlethread
fence"
=>
{
self
.atomic_fence
(
order
,
SynchronizationScope
::
Single
Thread
);
return
;
}
"singlethreadfence"
=>
{
bx
.atomic_fence
(
order
,
SynchronizationScope
::
SingleThread
);
return
;
}
// These are all AtomicRMW ops
op
=>
{
let
atom_op
=
match
op
{
"xchg"
=>
AtomicRmwBinOp
::
AtomicXchg
,
"xadd"
=>
AtomicRmwBinOp
::
AtomicAdd
,
"xsub"
=>
AtomicRmwBinOp
::
AtomicSub
,
"and"
=>
AtomicRmwBinOp
::
AtomicAnd
,
"nand"
=>
AtomicRmwBinOp
::
AtomicNand
,
"or"
=>
AtomicRmwBinOp
::
AtomicOr
,
"xor"
=>
AtomicRmwBinOp
::
AtomicXor
,
"max"
=>
AtomicRmwBinOp
::
AtomicMax
,
"min"
=>
AtomicRmwBinOp
::
AtomicMin
,
"umax"
=>
AtomicRmwBinOp
::
AtomicUMax
,
"umin"
=>
AtomicRmwBinOp
::
AtomicUMin
,
_
=>
cx
.sess
()
.fatal
(
"unknown atomic operation"
)
};
// These are all AtomicRMW ops
op
=>
{
let
atom_op
=
match
op
{
"xchg"
=>
AtomicRmwBinOp
::
AtomicXchg
,
"xadd"
=>
AtomicRmwBinOp
::
AtomicAdd
,
"xsub"
=>
AtomicRmwBinOp
::
AtomicSub
,
"and"
=>
AtomicRmwBinOp
::
AtomicAnd
,
"nand"
=>
AtomicRmwBinOp
::
AtomicNand
,
"or"
=>
AtomicRmwBinOp
::
AtomicOr
,
"xor"
=>
AtomicRmwBinOp
::
AtomicXor
,
"max"
=>
AtomicRmwBinOp
::
AtomicMax
,
"min"
=>
AtomicRmwBinOp
::
AtomicMin
,
"umax"
=>
AtomicRmwBinOp
::
AtomicUMax
,
"umin"
=>
AtomicRmwBinOp
::
AtomicUMin
,
_
=>
cx
.sess
()
.fatal
(
"unknown atomic operation"
)
};
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
bx
.atomic_rmw
(
atom_op
,
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
order
)
}
else
{
return
invalid_monomorphization
(
ty
);
let
ty
=
substs
.type_at
(
0
);
if
int_type_width_signed
(
ty
,
cx
)
.is_some
()
{
self
.atomic_rmw
(
atom_op
,
args
[
0
]
.immediate
(),
args
[
1
]
.immediate
(),
order
)
}
else
{
return
invalid_monomorphization
(
ty
);
}
}
}
}
}
"nontemporal_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
bx
.cx
());
args
[
1
]
.val
.nontemporal_store
(
bx
,
dst
);
return
;
}
_
=>
{
let
intr
=
Intrinsic
::
find
(
&
name
)
.unwrap_or_else
(||
bug!
(
"unknown intrinsic '{}'"
,
name
));
fn
one
<
T
>
(
x
:
Vec
<
T
>
)
->
T
{
assert_eq!
(
x
.len
(),
1
);
x
.into_iter
()
.next
()
.unwrap
()
"nontemporal_store"
=>
{
let
dst
=
args
[
0
]
.deref
(
cx
);
args
[
1
]
.val
.nontemporal_store
(
&
self
,
dst
);
return
;
}
fn
ty_to_type
(
cx
:
&
CodegenCx
<
'll
,
'_
>
,
t
:
&
intrinsics
::
Type
)
->
Vec
<&
'll
Type
>
{
use
intrinsics
::
Type
::
*
;
match
*
t
{
Void
=>
vec!
[
cx
.type_void
()],
Integer
(
_
signed
,
_
width
,
llvm_width
)
=>
{
vec!
[
cx
.type_ix
(
llvm_width
as
u64
)]
}
Float
(
x
)
=>
{
match
x
{
32
=>
vec!
[
cx
.type_f32
()],
64
=>
vec!
[
cx
.type_f64
()],
_
=>
bug!
()
_
=>
{
let
intr
=
match
Intrinsic
::
find
(
&
name
)
{
Some
(
intr
)
=>
intr
,
None
=>
bug!
(
"unknown intrinsic '{}'"
,
name
),
};
fn
one
<
T
>
(
x
:
Vec
<
T
>
)
->
T
{
assert_eq!
(
x
.len
(),
1
);
x
.into_iter
()
.next
()
.unwrap
()
}
fn
ty_to_type
<
'll
>
(
cx
:
&
CodegenCx
<
'll
,
'_
>
,
t
:
&
intrinsics
::
Type
)
->
Vec
<&
'll
Type
>
{
use
intrinsics
::
Type
::
*
;
match
*
t
{
Void
=>
vec!
[
cx
.type_void
()],
Integer
(
_
signed
,
_
width
,
llvm_width
)
=>
{
vec!
[
cx
.type_ix
(
llvm_width
as
u64
)]
}
Float
(
x
)
=>
{
match
x
{
32
=>
vec!
[
cx
.type_f32
()],
64
=>
vec!
[
cx
.type_f64
()],
_
=>
bug!
()
}
}
Pointer
(
ref
t
,
ref
llvm_elem
,
_
const
)
=>
{
let
t
=
llvm_elem
.as_ref
()
.unwrap_or
(
t
);
let
elem
=
one
(
ty_to_type
(
cx
,
t
));
vec!
[
cx
.type_ptr_to
(
elem
)]
}
Vector
(
ref
t
,
ref
llvm_elem
,
length
)
=>
{
let
t
=
llvm_elem
.as_ref
()
.unwrap_or
(
t
);
let
elem
=
one
(
ty_to_type
(
cx
,
t
));
vec!
[
cx
.type_vector
(
elem
,
length
as
u64
)]
}
Aggregate
(
false
,
ref
contents
)
=>
{
let
elems
=
contents
.iter
()
.map
(|
t
|
one
(
ty_to_type
(
cx
,
t
)))
.collect
::
<
Vec
<
_
>>
();
vec!
[
cx
.type_struct
(
&
elems
,
false
)]
}
Aggregate
(
true
,
ref
contents
)
=>
{
contents
.iter
()
.flat_map
(|
t
|
ty_to_type
(
cx
,
t
))
.collect
()
}
}
Pointer
(
ref
t
,
ref
llvm_elem
,
_
const
)
=>
{
let
t
=
llvm_elem
.as_ref
()
.unwrap_or
(
t
);
let
elem
=
one
(
ty_to_type
(
cx
,
t
));
vec!
[
cx
.type_ptr_to
(
elem
)]
}
Vector
(
ref
t
,
ref
llvm_elem
,
length
)
=>
{
let
t
=
llvm_elem
.as_ref
()
.unwrap_or
(
t
);
let
elem
=
one
(
ty_to_type
(
cx
,
t
));
vec!
[
cx
.type_vector
(
elem
,
length
as
u64
)]
}
Aggregate
(
false
,
ref
contents
)
=>
{
let
elems
=
contents
.iter
()
.map
(|
t
|
one
(
ty_to_type
(
cx
,
t
)))
.collect
::
<
Vec
<
_
>>
();
vec!
[
cx
.type_struct
(
&
elems
,
false
)]
}
Aggregate
(
true
,
ref
contents
)
=>
{
contents
.iter
()
.flat_map
(|
t
|
ty_to_type
(
cx
,
t
))
.collect
()
}
}
}
// This allows an argument list like `foo, (bar, baz),
// qux` to be converted into `foo, bar, baz, qux`, integer
// arguments to be truncated as needed and pointers to be
// cast.
fn
modify_as_needed
(
bx
:
&
Builder
<
'a
,
'll
,
'tcx
>
,
t
:
&
intrinsics
::
Type
,
arg
:
&
OperandRef
<
'tcx
,
&
'll
Value
>
,
)
->
Vec
<&
'll
Value
>
{
match
*
t
{
intrinsics
::
Type
::
Aggregate
(
true
,
ref
contents
)
=>
{
// We found a tuple that needs squishing! So
// run over the tuple and load each field.
//
// This assumes the type is "simple", i.e. no
// destructors, and the contents are SIMD
// etc.
assert
!
(
!
bx
.cx
()
.type_needs_drop
(
arg
.layout.ty
));
let
(
ptr
,
align
)
=
match
arg
.val
{
OperandValue
::
Ref
(
ptr
,
None
,
align
)
=>
(
ptr
,
align
),
_
=>
bug!
()
};
let
arg
=
PlaceRef
::
new_sized
(
ptr
,
arg
.layout
,
align
);
(
0
..
contents
.len
())
.map
(|
i
|
{
arg
.project_field
(
bx
,
i
)
.load
(
bx
)
.immediate
()
})
.collect
()
}
intrinsics
::
Type
::
Pointer
(
_
,
Some
(
ref
llvm_elem
),
_
)
=>
{
let
llvm_elem
=
one
(
ty_to_type
(
bx
.cx
(),
llvm_elem
));
vec!
[
bx
.pointercast
(
arg
.immediate
(),
bx
.cx
()
.type_ptr_to
(
llvm_elem
))]
}
intrinsics
::
Type
::
Vector
(
_
,
Some
(
ref
llvm_elem
),
length
)
=>
{
let
llvm_elem
=
one
(
ty_to_type
(
bx
.cx
(),
llvm_elem
));
vec!
[
bx
.bitcast
(
arg
.immediate
(),
bx
.cx
()
.type_vector
(
llvm_elem
,
length
as
u64
))
]
}
intrinsics
::
Type
::
Integer
(
_
,
width
,
llvm_width
)
if
width
!=
llvm_width
=>
{
// the LLVM intrinsic uses a smaller integer
// size than the C intrinsic's signature, so
// we have to trim it down here.
vec!
[
bx
.trunc
(
arg
.immediate
(),
bx
.cx
()
.type_ix
(
llvm_width
as
u64
))]
// This allows an argument list like `foo, (bar, baz),
// qux` to be converted into `foo, bar, baz, qux`, integer
// arguments to be truncated as needed and pointers to be
// cast.
fn
modify_as_needed
<
'll
,
'tcx
>
(
bx
:
&
Builder
<
'_
,
'll
,
'tcx
>
,
t
:
&
intrinsics
::
Type
,
arg
:
&
OperandRef
<
'tcx
,
&
'll
Value
>
,
)
->
Vec
<&
'll
Value
>
{
match
*
t
{
intrinsics
::
Type
::
Aggregate
(
true
,
ref
contents
)
=>
{
// We found a tuple that needs squishing! So
// run over the tuple and load each field.
//
// This assumes the type is "simple", i.e. no
// destructors, and the contents are SIMD
// etc.
assert
!
(
!
bx
.cx
()
.type_needs_drop
(
arg
.layout.ty
));
let
(
ptr
,
align
)
=
match
arg
.val
{
OperandValue
::
Ref
(
ptr
,
None
,
align
)
=>
(
ptr
,
align
),
_
=>
bug!
()
};
let
arg
=
PlaceRef
::
new_sized
(
ptr
,
arg
.layout
,
align
);
(
0
..
contents
.len
())
.map
(|
i
|
{
arg
.project_field
(
bx
,
i
)
.load
(
bx
)
.immediate
()
})
.collect
()
}
intrinsics
::
Type
::
Pointer
(
_
,
Some
(
ref
llvm_elem
),
_
)
=>
{
let
llvm_elem
=
one
(
ty_to_type
(
bx
.cx
(),
llvm_elem
));
vec!
[
bx
.pointercast
(
arg
.immediate
(),
bx
.cx
()
.type_ptr_to
(
llvm_elem
))]
}
intrinsics
::
Type
::
Vector
(
_
,
Some
(
ref
llvm_elem
),
length
)
=>
{
let
llvm_elem
=
one
(
ty_to_type
(
bx
.cx
(),
llvm_elem
));
vec!
[
bx
.bitcast
(
arg
.immediate
(),
bx
.cx
()
.type_vector
(
llvm_elem
,
length
as
u64
))
]
}
intrinsics
::
Type
::
Integer
(
_
,
width
,
llvm_width
)
if
width
!=
llvm_width
=>
{
// the LLVM intrinsic uses a smaller integer
// size than the C intrinsic's signature, so
// we have to trim it down here.
vec!
[
bx
.trunc
(
arg
.immediate
(),
bx
.cx
()
.type_ix
(
llvm_width
as
u64
))]
}
_
=>
vec!
[
arg
.immediate
()],
}
_
=>
vec!
[
arg
.immediate
()],
}
}
let
inputs
=
intr
.inputs
.iter
()
.flat_map
(|
t
|
ty_to_type
(
cx
,
t
))
.collect
::
<
Vec
<
_
>>
();
let
inputs
=
intr
.inputs
.iter
()
.flat_map
(|
t
|
ty_to_type
(
cx
,
t
))
.collect
::
<
Vec
<
_
>>
();
let
outputs
=
one
(
ty_to_type
(
cx
,
&
intr
.output
));
let
outputs
=
one
(
ty_to_type
(
cx
,
&
intr
.output
));
let
llargs
:
Vec
<
_
>
=
intr
.inputs
.iter
()
.zip
(
args
)
.flat_map
(|(
t
,
arg
)|
{
modify_as_needed
(
bx
,
t
,
arg
)
})
.collect
();
assert_eq!
(
inputs
.len
(),
llargs
.len
());
let
llargs
:
Vec
<
_
>
=
intr
.inputs
.iter
()
.zip
(
args
)
.flat_map
(|(
t
,
arg
)|
{
modify_as_needed
(
&
self
,
t
,
arg
)
})
.collect
();
assert_eq!
(
inputs
.len
(),
llargs
.len
());
let
val
=
match
intr
.definition
{
intrinsics
::
IntrinsicDef
::
Named
(
name
)
=>
{
let
f
=
declare
::
declare_cfn
(
cx
,
name
,
cx
.type_func
(
&
inputs
,
outputs
));
bx
.call
(
f
,
&
llargs
,
None
)
}
};
let
val
=
match
intr
.definition
{
intrinsics
::
IntrinsicDef
::
Named
(
name
)
=>
{
let
f
=
declare
::
declare_cfn
(
cx
,
name
,
cx
.type_func
(
&
inputs
,
outputs
));
self
.call
(
f
,
&
llargs
,
None
)
}
};
match
*
intr
.output
{
intrinsics
::
Type
::
Aggregate
(
flatten
,
ref
elems
)
=>
{
// the output is a tuple so we need to munge it properly
assert
!
(
!
flatten
);
match
*
intr
.output
{
intrinsics
::
Type
::
Aggregate
(
flatten
,
ref
elems
)
=>
{
// the output is a tuple so we need to munge it properly
assert
!
(
!
flatten
);
for
i
in
0
..
elems
.len
()
{
let
dest
=
result
.project_field
(
bx
,
i
);
let
val
=
bx
.extract_value
(
val
,
i
as
u64
);
bx
.store
(
val
,
dest
.llval
,
dest
.align
);
for
i
in
0
..
elems
.len
()
{
let
dest
=
result
.project_field
(
&
self
,
i
);
let
val
=
self
.extract_value
(
val
,
i
as
u64
);
self
.store
(
val
,
dest
.llval
,
dest
.align
);
}
return
;
}
return
;
_
=>
val
,
}
_
=>
val
,
}
}
};
};
if
!
fn_ty
.ret
.is_ignore
()
{
if
let
PassMode
::
Cast
(
ty
)
=
fn_ty
.ret.mode
{
let
ptr
=
bx
.pointercast
(
result
.llval
,
cx
.type_ptr_to
(
ty
.llvm_type
(
cx
)));
bx
.store
(
llval
,
ptr
,
result
.align
);
}
else
{
OperandRef
::
from_immediate_or_packed_pair
(
bx
,
llval
,
result
.layout
)
.val
.store
(
bx
,
result
);
if
!
fn_ty
.ret
.is_ignore
()
{
if
let
PassMode
::
Cast
(
ty
)
=
fn_ty
.ret.mode
{
let
ptr
=
self
.pointercast
(
result
.llval
,
cx
.type_ptr_to
(
ty
.llvm_type
(
cx
)));
self
.store
(
llval
,
ptr
,
result
.align
);
}
else
{
OperandRef
::
from_immediate_or_packed_pair
(
&
self
,
llval
,
result
.layout
)
.val
.store
(
&
self
,
result
);
}
}
}
}
...
...
src/librustc_codegen_llvm/mir/block.rs
浏览文件 @
0a1c5095
...
...
@@ -25,10 +25,7 @@
use
type_
::
Type
;
use
value
::
Value
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedTypeMethods
,
DerivedIntrinsicMethods
,
StaticMethods
,
};
use
interfaces
::
*
;
use
syntax
::
symbol
::
Symbol
;
use
syntax_pos
::
Pos
;
...
...
@@ -560,8 +557,6 @@ fn codegen_terminator(&mut self,
};
if
intrinsic
.is_some
()
&&
intrinsic
!=
Some
(
"drop_in_place"
)
{
use
intrinsic
::
codegen_intrinsic_call
;
let
dest
=
match
ret_dest
{
_
if
fn_ty
.ret
.is_indirect
()
=>
llargs
[
0
],
ReturnDest
::
Nothing
=>
{
...
...
@@ -628,8 +623,8 @@ fn codegen_terminator(&mut self,
let
callee_ty
=
instance
.as_ref
()
.unwrap
()
.ty
(
bx
.cx
()
.tcx
);
codegen_intrinsic_call
(
&
bx
,
callee_ty
,
&
fn_ty
,
&
args
,
dest
,
terminator
.source_info.span
);
&
bx
.codegen_intrinsic_call
(
callee_ty
,
&
fn_ty
,
&
args
,
dest
,
terminator
.source_info.span
);
if
let
ReturnDest
::
IndirectOperand
(
dst
,
_
)
=
ret_dest
{
self
.store_return
(
&
bx
,
ret_dest
,
&
fn_ty
.ret
,
dst
.llval
);
...
...
src/librustc_codegen_llvm/mir/operand.rs
浏览文件 @
0a1c5095
...
...
@@ -20,7 +20,7 @@
use
type_of
::
LayoutLlvmExt
;
use
glue
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedIntrinsic
Methods
};
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
IntrinsicDeclaration
Methods
};
use
std
::
fmt
;
...
...
src/librustc_codegen_llvm/mir/place.rs
浏览文件 @
0a1c5095
...
...
@@ -21,10 +21,7 @@
use
glue
;
use
mir
::
constant
::
const_alloc_to_llvm
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedTypeMethods
,
DerivedIntrinsicMethods
,
StaticMethods
,
};
use
interfaces
::
*
;
use
super
::{
FunctionCx
,
LocalRef
};
use
super
::
operand
::{
OperandRef
,
OperandValue
};
...
...
src/librustc_codegen_llvm/mir/rvalue.rs
浏览文件 @
0a1c5095
...
...
@@ -25,7 +25,7 @@
use
type_of
::
LayoutLlvmExt
;
use
value
::
Value
;
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
DerivedIntrinsic
Methods
};
use
interfaces
::{
BuilderMethods
,
ConstMethods
,
BaseTypeMethods
,
IntrinsicDeclaration
Methods
};
use
super
::{
FunctionCx
,
LocalRef
};
use
super
::
operand
::{
OperandRef
,
OperandValue
};
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录