-
Notifications
You must be signed in to change notification settings - Fork 564
/
Copy pathcasts.rs
210 lines (197 loc) · 8.73 KB
/
casts.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
use super::int::signed::{Sint16Type, Sint32Type, Sint64Type, Sint8Type};
use super::int::signed128::Sint128Type;
use super::int::unsigned::{Uint16Type, Uint32Type, Uint64Type, Uint8Type};
use super::int::unsigned128::Uint128Type;
use super::range_check::RangeCheckType;
use super::utils::reinterpret_cast_signature;
use crate::define_libfunc_hierarchy;
use crate::extensions::lib_func::{
BranchSignature, LibfuncSignature, OutputVarInfo, ParamSignature, SierraApChange,
SignatureOnlyGenericLibfunc, SignatureSpecializationContext, SpecializationContext,
};
use crate::extensions::{
args_as_two_types, NamedLibfunc, NamedType, OutputVarReferenceInfo,
SignatureBasedConcreteLibfunc, SpecializationError,
};
use crate::ids::ConcreteTypeId;
use crate::program::GenericArg;
define_libfunc_hierarchy! {
pub enum CastLibfunc {
Downcast(DowncastLibfunc),
Upcast(UpcastLibfunc),
}, CastConcreteLibfunc
}
/// The type of casting between two integer types.
#[derive(PartialEq, Eq)]
pub struct CastType {
/// Does the source type have values above the destination type possible values.
pub overflow_above: bool,
/// Does the source type have values below the destination type possible values.
pub overflow_below: bool,
}
pub struct IntTypeInfo {
pub nbits: usize,
pub signed: bool,
}
impl IntTypeInfo {
/// Returns the cast type.
pub fn cast_type(&self, to: &IntTypeInfo) -> CastType {
match (self.signed, to.signed) {
(false, false) => {
// Unsigned to unsigned.
// Overflow below is never possible, as the minimum value of both types is 0.
// Overflow above is possible if `self` has strictly more bits than the `to`.
// We use `>=` instead of `>` to provide backward compatibility with the case
// casting from a type to itself. TODO(orizi): Remove this backward compatibility at
// next major sierra version.
CastType { overflow_above: self.nbits >= to.nbits, overflow_below: false }
}
(true, true) => {
// Signed to signed.
// Both overflows are possible if `self` has strictly more bits than `to`.
let can_overflow = self.nbits > to.nbits;
CastType { overflow_above: can_overflow, overflow_below: can_overflow }
}
(true, false) => {
// Signed to unsigned.
// Overflow below is always possible, as the minimum value of `self` is lower than 0
// and of `to` is 0. Overflow above is possible if the `self` type
// has 2 bits more than `to` (as i8 to u7 cannot overflow, but i8 to u6 can).
CastType { overflow_above: self.nbits >= to.nbits + 2, overflow_below: true }
}
(false, true) => {
// Unsigned to signed.
// Overflow below is never possible, as the minimum value of `self` is 0 and of `to`
// lower than 0. Overflow above is possible if `self` has more bits
// than `to` (as u8 to i9 cannot overflow, but u8 to i8 can).
CastType { overflow_above: self.nbits >= to.nbits, overflow_below: false }
}
}
}
/// Returns true if this type can participate in downcasts.
fn downcastable(&self) -> bool {
// We don't support downcasting larger than 128-bit integers, as this would not reduce the
// need for range checks.
self.nbits <= 128
}
}
/// Returns a number of bits in a concrete integer type.
fn get_int_info(
context: &dyn SignatureSpecializationContext,
ty: ConcreteTypeId,
) -> Result<IntTypeInfo, SpecializationError> {
Ok(match context.get_type_info(ty)?.long_id.generic_id {
id if id == Uint8Type::ID => IntTypeInfo { nbits: 8, signed: false },
id if id == Sint8Type::ID => IntTypeInfo { nbits: 8, signed: true },
id if id == Uint16Type::ID => IntTypeInfo { nbits: 16, signed: false },
id if id == Sint16Type::ID => IntTypeInfo { nbits: 16, signed: true },
id if id == Uint32Type::ID => IntTypeInfo { nbits: 32, signed: false },
id if id == Sint32Type::ID => IntTypeInfo { nbits: 32, signed: true },
id if id == Uint64Type::ID => IntTypeInfo { nbits: 64, signed: false },
id if id == Sint64Type::ID => IntTypeInfo { nbits: 64, signed: true },
id if id == Uint128Type::ID => IntTypeInfo { nbits: 128, signed: false },
id if id == Sint128Type::ID => IntTypeInfo { nbits: 128, signed: true },
_ => return Err(SpecializationError::UnsupportedGenericArg),
})
}
/// Libfunc for casting from one type to another where any input value can fit into the destination
/// type. For example, from u8 to u64.
#[derive(Default)]
pub struct UpcastLibfunc {}
impl SignatureOnlyGenericLibfunc for UpcastLibfunc {
const STR_ID: &'static str = "upcast";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
let from_info = get_int_info(context, from_ty.clone())?;
let to_info = get_int_info(context, to_ty.clone())?;
let cast_type = from_info.cast_type(&to_info);
if cast_type.overflow_above || cast_type.overflow_below {
// Finding if the detected possible overflow is not actually possible, but a backward
// compatibility based one (as smae type can have no overflow).
// TODO(orizi): Remove this check after the backward compatibility is removed at next
// major sierra version.
if from_ty != to_ty {
return Err(SpecializationError::UnsupportedGenericArg);
}
}
Ok(reinterpret_cast_signature(from_ty, to_ty))
}
}
/// A concrete version of the `downcast` libfunc. See [DowncastLibfunc].
pub struct DowncastConcreteLibfunc {
pub signature: LibfuncSignature,
pub from_ty: ConcreteTypeId,
pub from_info: IntTypeInfo,
pub to_ty: ConcreteTypeId,
pub to_info: IntTypeInfo,
}
impl SignatureBasedConcreteLibfunc for DowncastConcreteLibfunc {
fn signature(&self) -> &LibfuncSignature {
&self.signature
}
}
/// Libfunc for casting from one type to another where the input value may not fit into the
/// destination type. For example, from u64 to u8.
#[derive(Default)]
pub struct DowncastLibfunc {}
impl NamedLibfunc for DowncastLibfunc {
type Concrete = DowncastConcreteLibfunc;
const STR_ID: &'static str = "downcast";
fn specialize_signature(
&self,
context: &dyn SignatureSpecializationContext,
args: &[GenericArg],
) -> Result<LibfuncSignature, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
let from_info = get_int_info(context, from_ty.clone())?;
let to_info = get_int_info(context, to_ty.clone())?;
if !from_info.downcastable() || !to_info.downcastable() {
return Err(SpecializationError::UnsupportedGenericArg);
}
let range_check_type = context.get_concrete_type(RangeCheckType::id(), &[])?;
let rc_output_info = OutputVarInfo::new_builtin(range_check_type.clone(), 0);
Ok(LibfuncSignature {
param_signatures: vec![
ParamSignature::new(range_check_type).with_allow_add_const(),
ParamSignature::new(from_ty),
],
branch_signatures: vec![
// Success.
BranchSignature {
vars: vec![
rc_output_info.clone(),
OutputVarInfo {
ty: to_ty,
ref_info: OutputVarReferenceInfo::SameAsParam { param_idx: 1 },
},
],
ap_change: SierraApChange::Known { new_vars_only: false },
},
// Failure.
BranchSignature {
vars: vec![rc_output_info],
ap_change: SierraApChange::Known { new_vars_only: false },
},
],
fallthrough: Some(0),
})
}
fn specialize(
&self,
context: &dyn SpecializationContext,
args: &[GenericArg],
) -> Result<Self::Concrete, SpecializationError> {
let (from_ty, to_ty) = args_as_two_types(args)?;
Ok(DowncastConcreteLibfunc {
signature: self.specialize_signature(context.upcast(), args)?,
from_info: get_int_info(context.upcast(), from_ty.clone())?,
from_ty,
to_info: get_int_info(context.upcast(), to_ty.clone())?,
to_ty,
})
}
}