1 // Copyright (c) Facebook, Inc. and its affiliates.
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
8 use crate::instr::Hhbc;
9 use crate::instr::Predicate;
10 use crate::type_struct::TypeStruct;
13 use crate::EnforceableType;
14 use crate::FuncBuilder;
15 use crate::FunctionId;
19 use crate::TypeConstraintFlags;
20 use crate::TypeStructResolveOp;
23 /// Helpers for emitting more complex constructs.
24 pub trait FuncBuilderEx {
26 /// jmp_t true_br, false_br
32 fn emit_if_then(&mut self, pred: ValueId, loc: LocId, f: impl FnOnce(&mut Self));
34 /// Build a conditional:
37 /// Note that although the callback only returns a single Instr it can emit
38 /// as many instrs as it wants simply returning the final one. In addition
39 /// it can return a tombstone to indicate that it already emitted everything
45 f_true: impl FnOnce(&mut Self) -> Instr,
46 f_false: impl FnOnce(&mut Self) -> Instr,
49 /// Emit a constant string.
50 fn emit_string(&mut self, s: &str) -> ValueId;
52 /// Build an `is` check.
53 fn is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> Instr;
55 /// Emit an `is` check. This behaves like `is()` but instead of returning
56 /// the final Instr it emits it.
57 fn emit_is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> ValueId;
59 fn todo_fake_instr(&mut self, reason: &str, loc: LocId) -> Instr;
61 fn emit_todo_fake_instr(&mut self, reason: &str, loc: LocId) -> ValueId;
64 impl<'a> FuncBuilderEx for FuncBuilder<'a> {
65 fn emit_if_then(&mut self, pred: ValueId, loc: LocId, f: impl FnOnce(&mut Self)) {
66 let join_bid = self.alloc_bid();
67 let true_bid = self.alloc_bid();
68 self.emit(Instr::jmp_op(
75 self.start_block(true_bid);
77 if !self.func.is_terminated(self.cur_bid()) {
78 self.emit(Instr::jmp(join_bid, loc));
80 self.start_block(join_bid);
87 f_true: impl FnOnce(&mut Self) -> Instr,
88 f_false: impl FnOnce(&mut Self) -> Instr,
98 let mut join_bid = None;
99 let true_bid = self.alloc_bid();
100 let false_bid = self.alloc_bid();
101 self.emit(Instr::jmp_op(
109 self.start_block(true_bid);
110 let instr = f_true(self);
111 if !instr.is_tombstone() {
112 let terminated = matches!(instr, Instr::Terminator(_));
113 let arg = self.emit(instr);
115 let target = self.alloc_bid();
116 join_bid = Some(target);
117 self.emit(Instr::jmp_args(target, &[arg], loc));
121 self.start_block(false_bid);
122 let instr = f_false(self);
123 if !instr.is_tombstone() {
124 let terminated = matches!(instr, Instr::Terminator(_));
125 let arg = self.emit(instr);
127 let target = join_bid.get_or_insert_with(|| self.alloc_bid());
128 self.emit(Instr::jmp_args(*target, &[arg], loc));
132 if let Some(join_bid) = join_bid {
133 self.start_block(join_bid);
140 fn emit_string(&mut self, s: &str) -> ValueId {
141 self.emit_constant(Constant::String(self.strings.intern_str(s)))
144 fn is(&mut self, vid: ValueId, ety: &EnforceableType, loc: LocId) -> Instr {
145 if ety.modifiers == TypeConstraintFlags::NoFlags
146 || ety.modifiers == TypeConstraintFlags::ExtendedHint
149 BaseType::AnyArray => self.todo_fake_instr("BaseType::AnyArray", loc),
150 BaseType::Arraykey => self.todo_fake_instr("BaseType::Arraykey", loc),
151 BaseType::Bool => self.todo_fake_instr("BaseType::Bool", loc),
152 BaseType::Class(cid) => {
153 let constant = Constant::Array(Arc::new(
154 TypeStruct::Unresolved(cid).into_typed_value(&self.strings),
156 let adata = self.emit_constant(constant);
157 Instr::Hhbc(Hhbc::IsTypeStructC(
159 TypeStructResolveOp::Resolve,
163 BaseType::Classname => self.todo_fake_instr("BaseType::Classname", loc),
164 BaseType::Darray => self.todo_fake_instr("BaseType::Darray", loc),
165 BaseType::Dict => self.todo_fake_instr("BaseType::Dict", loc),
166 BaseType::Float => self.todo_fake_instr("BaseType::Float", loc),
167 BaseType::Int => Instr::Hhbc(Hhbc::IsTypeC(vid, IsTypeOp::Int, loc)),
168 BaseType::Keyset => self.todo_fake_instr("BaseType::Keyset", loc),
169 BaseType::Mixed => self.todo_fake_instr("BaseType::Mixed", loc),
170 BaseType::None => self.todo_fake_instr("BaseType::None", loc),
171 BaseType::Nonnull => self.todo_fake_instr("BaseType::Nonnull", loc),
172 BaseType::Noreturn => self.todo_fake_instr("BaseType::Noreturn", loc),
173 BaseType::Nothing => self.todo_fake_instr("BaseType::Nothing", loc),
174 BaseType::Null => self.todo_fake_instr("BaseType::Null", loc),
175 BaseType::Num => self.todo_fake_instr("BaseType::Num", loc),
176 BaseType::Resource => self.todo_fake_instr("BaseType::Resource", loc),
177 BaseType::String => Instr::Hhbc(Hhbc::IsTypeC(vid, IsTypeOp::Str, loc)),
178 BaseType::This => Instr::Hhbc(Hhbc::IsLateBoundCls(vid, loc)),
179 BaseType::Typename => self.todo_fake_instr("BaseType::Typename", loc),
180 BaseType::Varray => self.todo_fake_instr("BaseType::Varray", loc),
181 BaseType::VarrayOrDarray => self.todo_fake_instr("BaseType::VarrayOrDarray", loc),
182 BaseType::Vec => self.todo_fake_instr("BaseType::Vec", loc),
183 BaseType::VecOrDict => self.todo_fake_instr("BaseType::VecOrDict", loc),
184 BaseType::Void => self.todo_fake_instr("BaseType::Void", loc),
187 self.todo_fake_instr(&format!("Unhandled modifiers: {:?}", ety.modifiers), loc)
191 fn emit_is(&mut self, vid: ValueId, ty: &EnforceableType, loc: LocId) -> ValueId {
192 let instr = self.is(vid, ty, loc);
196 fn todo_fake_instr(&mut self, reason: &str, loc: LocId) -> Instr {
197 let op = self.emit_string(reason);
198 let fid = FunctionId::from_str("todo", &self.strings);
199 Instr::simple_call(fid, &[op], loc)
202 fn emit_todo_fake_instr(&mut self, reason: &str, loc: LocId) -> ValueId {
203 let instr = self.todo_fake_instr(reason, loc);