| 1 | // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/compiler/frontend/base_flow_graph_builder.h" |
| 6 | |
| 7 | #include "vm/compiler/ffi/call.h" |
| 8 | #include "vm/compiler/frontend/flow_graph_builder.h" // For InlineExitCollector. |
| 9 | #include "vm/compiler/jit/compiler.h" // For Compiler::IsBackgroundCompilation(). |
| 10 | #include "vm/compiler/runtime_api.h" |
| 11 | #include "vm/growable_array.h" |
| 12 | #include "vm/object_store.h" |
| 13 | |
| 14 | namespace dart { |
| 15 | namespace kernel { |
| 16 | |
| 17 | #define Z (zone_) |
| 18 | #define I (thread_->isolate()) |
| 19 | |
| 20 | Fragment& Fragment::operator+=(const Fragment& other) { |
| 21 | if (entry == NULL) { |
| 22 | entry = other.entry; |
| 23 | current = other.current; |
| 24 | } else if (current != NULL && other.entry != NULL) { |
| 25 | current->LinkTo(other.entry); |
| 26 | current = other.current; |
| 27 | } |
| 28 | return *this; |
| 29 | } |
| 30 | |
| 31 | Fragment& Fragment::operator<<=(Instruction* next) { |
| 32 | if (entry == NULL) { |
| 33 | entry = current = next; |
| 34 | } else if (current != NULL) { |
| 35 | current->LinkTo(next); |
| 36 | current = next; |
| 37 | } |
| 38 | return *this; |
| 39 | } |
| 40 | |
| 41 | void Fragment::Prepend(Instruction* start) { |
| 42 | if (entry == NULL) { |
| 43 | entry = current = start; |
| 44 | } else { |
| 45 | start->LinkTo(entry); |
| 46 | entry = start; |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | Fragment Fragment::closed() { |
| 51 | ASSERT(entry != NULL); |
| 52 | return Fragment(entry, NULL); |
| 53 | } |
| 54 | |
| 55 | Fragment operator+(const Fragment& first, const Fragment& second) { |
| 56 | Fragment result = first; |
| 57 | result += second; |
| 58 | return result; |
| 59 | } |
| 60 | |
| 61 | Fragment operator<<(const Fragment& fragment, Instruction* next) { |
| 62 | Fragment result = fragment; |
| 63 | result <<= next; |
| 64 | return result; |
| 65 | } |
| 66 | |
| 67 | TestFragment::TestFragment(Instruction* entry, BranchInstr* branch) |
| 68 | : entry(entry), |
| 69 | true_successor_addresses(new SuccessorAddressArray(1)), |
| 70 | false_successor_addresses(new SuccessorAddressArray(1)) { |
| 71 | true_successor_addresses->Add(branch->true_successor_address()); |
| 72 | false_successor_addresses->Add(branch->false_successor_address()); |
| 73 | } |
| 74 | |
| 75 | void TestFragment::ConnectBranchesTo( |
| 76 | BaseFlowGraphBuilder* builder, |
| 77 | const TestFragment::SuccessorAddressArray& branches, |
| 78 | JoinEntryInstr* join) { |
| 79 | ASSERT(!branches.is_empty()); |
| 80 | for (auto branch : branches) { |
| 81 | *branch = builder->BuildTargetEntry(); |
| 82 | (*branch)->Goto(join); |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | BlockEntryInstr* TestFragment::CreateSuccessorFor( |
| 87 | BaseFlowGraphBuilder* builder, |
| 88 | const TestFragment::SuccessorAddressArray& branches) { |
| 89 | ASSERT(!branches.is_empty()); |
| 90 | |
| 91 | if (branches.length() == 1) { |
| 92 | TargetEntryInstr* target = builder->BuildTargetEntry(); |
| 93 | *(branches[0]) = target; |
| 94 | return target; |
| 95 | } |
| 96 | |
| 97 | JoinEntryInstr* join = builder->BuildJoinEntry(); |
| 98 | ConnectBranchesTo(builder, branches, join); |
| 99 | return join; |
| 100 | } |
| 101 | |
| 102 | BlockEntryInstr* TestFragment::CreateTrueSuccessor( |
| 103 | BaseFlowGraphBuilder* builder) { |
| 104 | ASSERT(true_successor_addresses != nullptr); |
| 105 | return CreateSuccessorFor(builder, *true_successor_addresses); |
| 106 | } |
| 107 | |
| 108 | BlockEntryInstr* TestFragment::CreateFalseSuccessor( |
| 109 | BaseFlowGraphBuilder* builder) { |
| 110 | ASSERT(false_successor_addresses != nullptr); |
| 111 | return CreateSuccessorFor(builder, *false_successor_addresses); |
| 112 | } |
| 113 | |
| 114 | Fragment BaseFlowGraphBuilder::LoadContextAt(int depth) { |
| 115 | intptr_t delta = context_depth_ - depth; |
| 116 | ASSERT(delta >= 0); |
| 117 | Fragment instructions = LoadLocal(parsed_function_->current_context_var()); |
| 118 | while (delta-- > 0) { |
| 119 | instructions += LoadNativeField(Slot::Context_parent()); |
| 120 | } |
| 121 | return instructions; |
| 122 | } |
| 123 | |
| 124 | Fragment BaseFlowGraphBuilder::StrictCompare(TokenPosition position, |
| 125 | Token::Kind kind, |
| 126 | bool number_check /* = false */) { |
| 127 | Value* right = Pop(); |
| 128 | Value* left = Pop(); |
| 129 | StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
| 130 | position, kind, left, right, number_check, GetNextDeoptId()); |
| 131 | Push(compare); |
| 132 | return Fragment(compare); |
| 133 | } |
| 134 | |
| 135 | Fragment BaseFlowGraphBuilder::StrictCompare(Token::Kind kind, |
| 136 | bool number_check /* = false */) { |
| 137 | Value* right = Pop(); |
| 138 | Value* left = Pop(); |
| 139 | StrictCompareInstr* compare = |
| 140 | new (Z) StrictCompareInstr(TokenPosition::kNoSource, kind, left, right, |
| 141 | number_check, GetNextDeoptId()); |
| 142 | Push(compare); |
| 143 | return Fragment(compare); |
| 144 | } |
| 145 | |
| 146 | Fragment BaseFlowGraphBuilder::BranchIfTrue(TargetEntryInstr** then_entry, |
| 147 | TargetEntryInstr** otherwise_entry, |
| 148 | bool negate) { |
| 149 | Fragment instructions = Constant(Bool::True()); |
| 150 | return instructions + BranchIfEqual(then_entry, otherwise_entry, negate); |
| 151 | } |
| 152 | |
| 153 | Fragment BaseFlowGraphBuilder::BranchIfNull(TargetEntryInstr** then_entry, |
| 154 | TargetEntryInstr** otherwise_entry, |
| 155 | bool negate) { |
| 156 | Fragment instructions = NullConstant(); |
| 157 | return instructions + BranchIfEqual(then_entry, otherwise_entry, negate); |
| 158 | } |
| 159 | |
| 160 | Fragment BaseFlowGraphBuilder::BranchIfEqual(TargetEntryInstr** then_entry, |
| 161 | TargetEntryInstr** otherwise_entry, |
| 162 | bool negate) { |
| 163 | Value* right_value = Pop(); |
| 164 | Value* left_value = Pop(); |
| 165 | StrictCompareInstr* compare = new (Z) StrictCompareInstr( |
| 166 | TokenPosition::kNoSource, negate ? Token::kNE_STRICT : Token::kEQ_STRICT, |
| 167 | left_value, right_value, false, GetNextDeoptId()); |
| 168 | BranchInstr* branch = new (Z) BranchInstr(compare, GetNextDeoptId()); |
| 169 | *then_entry = *branch->true_successor_address() = BuildTargetEntry(); |
| 170 | *otherwise_entry = *branch->false_successor_address() = BuildTargetEntry(); |
| 171 | return Fragment(branch).closed(); |
| 172 | } |
| 173 | |
| 174 | Fragment BaseFlowGraphBuilder::BranchIfStrictEqual( |
| 175 | TargetEntryInstr** then_entry, |
| 176 | TargetEntryInstr** otherwise_entry) { |
| 177 | Value* rhs = Pop(); |
| 178 | Value* lhs = Pop(); |
| 179 | StrictCompareInstr* compare = |
| 180 | new (Z) StrictCompareInstr(TokenPosition::kNoSource, Token::kEQ_STRICT, |
| 181 | lhs, rhs, false, GetNextDeoptId()); |
| 182 | BranchInstr* branch = new (Z) BranchInstr(compare, GetNextDeoptId()); |
| 183 | *then_entry = *branch->true_successor_address() = BuildTargetEntry(); |
| 184 | *otherwise_entry = *branch->false_successor_address() = BuildTargetEntry(); |
| 185 | return Fragment(branch).closed(); |
| 186 | } |
| 187 | |
| 188 | Fragment BaseFlowGraphBuilder::Return(TokenPosition position, |
| 189 | intptr_t yield_index) { |
| 190 | Fragment instructions; |
| 191 | |
| 192 | Value* value = Pop(); |
| 193 | ASSERT(stack_ == nullptr); |
| 194 | const Function& function = parsed_function_->function(); |
| 195 | Representation representation; |
| 196 | if (function.has_unboxed_integer_return()) { |
| 197 | representation = kUnboxedInt64; |
| 198 | } else if (function.has_unboxed_double_return()) { |
| 199 | representation = kUnboxedDouble; |
| 200 | } else { |
| 201 | ASSERT(!function.has_unboxed_return()); |
| 202 | representation = kTagged; |
| 203 | } |
| 204 | ReturnInstr* return_instr = new (Z) ReturnInstr( |
| 205 | position, value, GetNextDeoptId(), yield_index, representation); |
| 206 | if (exit_collector_ != nullptr) exit_collector_->AddExit(return_instr); |
| 207 | |
| 208 | instructions <<= return_instr; |
| 209 | |
| 210 | return instructions.closed(); |
| 211 | } |
| 212 | |
| 213 | Fragment BaseFlowGraphBuilder::CheckStackOverflow(TokenPosition position, |
| 214 | intptr_t stack_depth, |
| 215 | intptr_t loop_depth) { |
| 216 | return Fragment(new (Z) CheckStackOverflowInstr( |
| 217 | position, stack_depth, loop_depth, GetNextDeoptId(), |
| 218 | CheckStackOverflowInstr::kOsrAndPreemption)); |
| 219 | } |
| 220 | |
| 221 | Fragment BaseFlowGraphBuilder::CheckStackOverflowInPrologue( |
| 222 | TokenPosition position) { |
| 223 | if (IsInlining()) { |
| 224 | // If we are inlining don't actually attach the stack check. We must still |
| 225 | // create the stack check in order to allocate a deopt id. |
| 226 | CheckStackOverflow(position, 0, 0); |
| 227 | return Fragment(); |
| 228 | } |
| 229 | return CheckStackOverflow(position, 0, 0); |
| 230 | } |
| 231 | |
| 232 | Fragment BaseFlowGraphBuilder::Constant(const Object& value) { |
| 233 | ASSERT(value.IsNotTemporaryScopedHandle()); |
| 234 | ConstantInstr* constant = new (Z) ConstantInstr(value); |
| 235 | Push(constant); |
| 236 | return Fragment(constant); |
| 237 | } |
| 238 | |
| 239 | Fragment BaseFlowGraphBuilder::Goto(JoinEntryInstr* destination) { |
| 240 | return Fragment(new (Z) GotoInstr(destination, GetNextDeoptId())).closed(); |
| 241 | } |
| 242 | |
| 243 | Fragment BaseFlowGraphBuilder::IntConstant(int64_t value) { |
| 244 | return Fragment( |
| 245 | Constant(Integer::ZoneHandle(Z, Integer::New(value, Heap::kOld)))); |
| 246 | } |
| 247 | |
| 248 | Fragment BaseFlowGraphBuilder::MemoryCopy(classid_t src_cid, |
| 249 | classid_t dest_cid) { |
| 250 | Value* length = Pop(); |
| 251 | Value* dest_start = Pop(); |
| 252 | Value* src_start = Pop(); |
| 253 | Value* dest = Pop(); |
| 254 | Value* src = Pop(); |
| 255 | auto copy = new (Z) MemoryCopyInstr(src, dest, src_start, dest_start, length, |
| 256 | src_cid, dest_cid); |
| 257 | return Fragment(copy); |
| 258 | } |
| 259 | |
| 260 | Fragment BaseFlowGraphBuilder::TailCall(const Code& code) { |
| 261 | Value* arg_desc = Pop(); |
| 262 | return Fragment(new (Z) TailCallInstr(code, arg_desc)); |
| 263 | } |
| 264 | |
| 265 | void BaseFlowGraphBuilder::InlineBailout(const char* reason) { |
| 266 | if (IsInlining()) { |
| 267 | parsed_function_->function().set_is_inlinable(false); |
| 268 | parsed_function_->Bailout("kernel::BaseFlowGraphBuilder" , reason); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | Fragment BaseFlowGraphBuilder::TestTypeArgsLen(Fragment eq_branch, |
| 273 | Fragment neq_branch, |
| 274 | intptr_t num_type_args) { |
| 275 | Fragment test; |
| 276 | |
| 277 | TargetEntryInstr* eq_entry; |
| 278 | TargetEntryInstr* neq_entry; |
| 279 | |
| 280 | test += LoadArgDescriptor(); |
| 281 | test += LoadNativeField(Slot::ArgumentsDescriptor_type_args_len()); |
| 282 | test += IntConstant(num_type_args); |
| 283 | test += BranchIfEqual(&eq_entry, &neq_entry); |
| 284 | |
| 285 | eq_branch.Prepend(eq_entry); |
| 286 | neq_branch.Prepend(neq_entry); |
| 287 | |
| 288 | JoinEntryInstr* join = BuildJoinEntry(); |
| 289 | eq_branch += Goto(join); |
| 290 | neq_branch += Goto(join); |
| 291 | |
| 292 | return Fragment(test.entry, join); |
| 293 | } |
| 294 | |
| 295 | Fragment BaseFlowGraphBuilder::TestDelayedTypeArgs(LocalVariable* closure, |
| 296 | Fragment present, |
| 297 | Fragment absent) { |
| 298 | Fragment test; |
| 299 | |
| 300 | TargetEntryInstr* absent_entry; |
| 301 | TargetEntryInstr* present_entry; |
| 302 | |
| 303 | test += LoadLocal(closure); |
| 304 | test += LoadNativeField(Slot::Closure_delayed_type_arguments()); |
| 305 | test += Constant(Object::empty_type_arguments()); |
| 306 | test += BranchIfEqual(&absent_entry, &present_entry); |
| 307 | |
| 308 | present.Prepend(present_entry); |
| 309 | absent.Prepend(absent_entry); |
| 310 | |
| 311 | JoinEntryInstr* join = BuildJoinEntry(); |
| 312 | absent += Goto(join); |
| 313 | present += Goto(join); |
| 314 | |
| 315 | return Fragment(test.entry, join); |
| 316 | } |
| 317 | |
| 318 | Fragment BaseFlowGraphBuilder::TestAnyTypeArgs(Fragment present, |
| 319 | Fragment absent) { |
| 320 | if (parsed_function_->function().IsClosureFunction()) { |
| 321 | LocalVariable* closure = parsed_function_->ParameterVariable(0); |
| 322 | |
| 323 | JoinEntryInstr* complete = BuildJoinEntry(); |
| 324 | JoinEntryInstr* present_entry = BuildJoinEntry(); |
| 325 | |
| 326 | Fragment test = TestTypeArgsLen( |
| 327 | TestDelayedTypeArgs(closure, Goto(present_entry), absent), |
| 328 | Goto(present_entry), 0); |
| 329 | test += Goto(complete); |
| 330 | |
| 331 | Fragment(present_entry) + present + Goto(complete); |
| 332 | |
| 333 | return Fragment(test.entry, complete); |
| 334 | } else { |
| 335 | return TestTypeArgsLen(absent, present, 0); |
| 336 | } |
| 337 | } |
| 338 | |
| 339 | Fragment BaseFlowGraphBuilder::LoadIndexed(intptr_t index_scale) { |
| 340 | Value* index = Pop(); |
| 341 | Value* array = Pop(); |
| 342 | LoadIndexedInstr* instr = new (Z) LoadIndexedInstr( |
| 343 | array, index, /*index_unboxed=*/false, index_scale, kArrayCid, |
| 344 | kAlignedAccess, DeoptId::kNone, TokenPosition::kNoSource); |
| 345 | Push(instr); |
| 346 | return Fragment(instr); |
| 347 | } |
| 348 | |
| 349 | Fragment BaseFlowGraphBuilder::LoadIndexedTypedData(classid_t class_id, |
| 350 | intptr_t index_scale, |
| 351 | bool index_unboxed) { |
| 352 | // We use C behavior when dereferencing pointers, we assume alignment. |
| 353 | const AlignmentType alignment = kAlignedAccess; |
| 354 | |
| 355 | Value* index = Pop(); |
| 356 | Value* c_pointer = Pop(); |
| 357 | LoadIndexedInstr* instr = new (Z) |
| 358 | LoadIndexedInstr(c_pointer, index, index_unboxed, index_scale, class_id, |
| 359 | alignment, DeoptId::kNone, TokenPosition::kNoSource); |
| 360 | Push(instr); |
| 361 | return Fragment(instr); |
| 362 | } |
| 363 | |
| 364 | Fragment BaseFlowGraphBuilder::LoadUntagged(intptr_t offset) { |
| 365 | Value* object = Pop(); |
| 366 | auto load = new (Z) LoadUntaggedInstr(object, offset); |
| 367 | Push(load); |
| 368 | return Fragment(load); |
| 369 | } |
| 370 | |
| 371 | Fragment BaseFlowGraphBuilder::StoreUntagged(intptr_t offset) { |
| 372 | Value* value = Pop(); |
| 373 | Value* object = Pop(); |
| 374 | auto store = new (Z) StoreUntaggedInstr(object, value, offset); |
| 375 | return Fragment(store); |
| 376 | } |
| 377 | |
| 378 | Fragment BaseFlowGraphBuilder::ConvertUntaggedToUnboxed( |
| 379 | Representation to_representation) { |
| 380 | ASSERT(to_representation == kUnboxedIntPtr || |
| 381 | to_representation == kUnboxedFfiIntPtr); |
| 382 | Value* value = Pop(); |
| 383 | auto converted = new (Z) |
| 384 | IntConverterInstr(kUntagged, to_representation, value, DeoptId::kNone); |
| 385 | converted->mark_truncating(); |
| 386 | Push(converted); |
| 387 | return Fragment(converted); |
| 388 | } |
| 389 | |
| 390 | Fragment BaseFlowGraphBuilder::ConvertUnboxedToUntagged( |
| 391 | Representation from_representation) { |
| 392 | ASSERT(from_representation == kUnboxedIntPtr || |
| 393 | from_representation == kUnboxedFfiIntPtr); |
| 394 | Value* value = Pop(); |
| 395 | auto converted = new (Z) |
| 396 | IntConverterInstr(from_representation, kUntagged, value, DeoptId::kNone); |
| 397 | converted->mark_truncating(); |
| 398 | Push(converted); |
| 399 | return Fragment(converted); |
| 400 | } |
| 401 | |
| 402 | Fragment BaseFlowGraphBuilder::AddIntptrIntegers() { |
| 403 | Value* right = Pop(); |
| 404 | Value* left = Pop(); |
| 405 | #if defined(TARGET_ARCH_ARM64) || defined(TARGET_ARCH_X64) |
| 406 | auto add = new (Z) BinaryInt64OpInstr( |
| 407 | Token::kADD, left, right, DeoptId::kNone, Instruction::kNotSpeculative); |
| 408 | #else |
| 409 | auto add = |
| 410 | new (Z) BinaryInt32OpInstr(Token::kADD, left, right, DeoptId::kNone); |
| 411 | #endif |
| 412 | add->mark_truncating(); |
| 413 | Push(add); |
| 414 | return Fragment(add); |
| 415 | } |
| 416 | |
| 417 | Fragment BaseFlowGraphBuilder::UnboxSmiToIntptr() { |
| 418 | Value* value = Pop(); |
| 419 | auto untagged = new (Z) |
| 420 | UnboxIntegerInstr(kUnboxedIntPtr, UnboxIntegerInstr::kNoTruncation, value, |
| 421 | DeoptId::kNone, Instruction::kNotSpeculative); |
| 422 | Push(untagged); |
| 423 | return Fragment(untagged); |
| 424 | } |
| 425 | |
| 426 | Fragment BaseFlowGraphBuilder::FloatToDouble() { |
| 427 | Value* value = Pop(); |
| 428 | FloatToDoubleInstr* instr = new FloatToDoubleInstr(value, DeoptId::kNone); |
| 429 | Push(instr); |
| 430 | return Fragment(instr); |
| 431 | } |
| 432 | |
| 433 | Fragment BaseFlowGraphBuilder::DoubleToFloat() { |
| 434 | Value* value = Pop(); |
| 435 | DoubleToFloatInstr* instr = new DoubleToFloatInstr( |
| 436 | value, DeoptId::kNone, Instruction::SpeculativeMode::kNotSpeculative); |
| 437 | Push(instr); |
| 438 | return Fragment(instr); |
| 439 | } |
| 440 | |
| 441 | Fragment BaseFlowGraphBuilder::LoadField(const Field& field, |
| 442 | bool calls_initializer) { |
| 443 | return LoadNativeField(Slot::Get(MayCloneField(field), parsed_function_), |
| 444 | calls_initializer); |
| 445 | } |
| 446 | |
| 447 | Fragment BaseFlowGraphBuilder::LoadNativeField(const Slot& native_field, |
| 448 | bool calls_initializer) { |
| 449 | LoadFieldInstr* load = new (Z) LoadFieldInstr( |
| 450 | Pop(), native_field, TokenPosition::kNoSource, calls_initializer, |
| 451 | calls_initializer ? GetNextDeoptId() : DeoptId::kNone); |
| 452 | Push(load); |
| 453 | return Fragment(load); |
| 454 | } |
| 455 | |
| 456 | Fragment BaseFlowGraphBuilder::LoadLocal(LocalVariable* variable) { |
| 457 | ASSERT(!variable->is_captured()); |
| 458 | LoadLocalInstr* load = |
| 459 | new (Z) LoadLocalInstr(*variable, TokenPosition::kNoSource); |
| 460 | Push(load); |
| 461 | return Fragment(load); |
| 462 | } |
| 463 | |
| 464 | Fragment BaseFlowGraphBuilder::NullConstant() { |
| 465 | return Constant(Instance::ZoneHandle(Z, Instance::null())); |
| 466 | } |
| 467 | |
| 468 | Fragment BaseFlowGraphBuilder::GuardFieldLength(const Field& field, |
| 469 | intptr_t deopt_id) { |
| 470 | return Fragment(new (Z) GuardFieldLengthInstr(Pop(), field, deopt_id)); |
| 471 | } |
| 472 | |
| 473 | Fragment BaseFlowGraphBuilder::GuardFieldClass(const Field& field, |
| 474 | intptr_t deopt_id) { |
| 475 | return Fragment(new (Z) GuardFieldClassInstr(Pop(), field, deopt_id)); |
| 476 | } |
| 477 | |
| 478 | const Field& BaseFlowGraphBuilder::MayCloneField(const Field& field) { |
| 479 | if ((Compiler::IsBackgroundCompilation() || |
| 480 | FLAG_force_clone_compiler_objects) && |
| 481 | field.IsOriginal()) { |
| 482 | return Field::ZoneHandle(Z, field.CloneFromOriginal()); |
| 483 | } else { |
| 484 | ASSERT(field.IsZoneHandle()); |
| 485 | return field; |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | Fragment BaseFlowGraphBuilder::StoreInstanceField( |
| 490 | TokenPosition position, |
| 491 | const Slot& field, |
| 492 | StoreInstanceFieldInstr::Kind |
| 493 | kind /* = StoreInstanceFieldInstr::Kind::kOther */, |
| 494 | StoreBarrierType emit_store_barrier /* = kEmitStoreBarrier */) { |
| 495 | Value* value = Pop(); |
| 496 | if (value->BindsToConstant()) { |
| 497 | emit_store_barrier = kNoStoreBarrier; |
| 498 | } |
| 499 | StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr( |
| 500 | field, Pop(), value, emit_store_barrier, position, kind); |
| 501 | return Fragment(store); |
| 502 | } |
| 503 | |
| 504 | Fragment BaseFlowGraphBuilder::StoreInstanceField( |
| 505 | const Field& field, |
| 506 | StoreInstanceFieldInstr::Kind |
| 507 | kind /* = StoreInstanceFieldInstr::Kind::kOther */, |
| 508 | StoreBarrierType emit_store_barrier) { |
| 509 | Value* value = Pop(); |
| 510 | if (value->BindsToConstant()) { |
| 511 | emit_store_barrier = kNoStoreBarrier; |
| 512 | } |
| 513 | |
| 514 | StoreInstanceFieldInstr* store = new (Z) StoreInstanceFieldInstr( |
| 515 | MayCloneField(field), Pop(), value, emit_store_barrier, |
| 516 | TokenPosition::kNoSource, parsed_function_, kind); |
| 517 | |
| 518 | return Fragment(store); |
| 519 | } |
| 520 | |
| 521 | Fragment BaseFlowGraphBuilder::StoreInstanceFieldGuarded( |
| 522 | const Field& field, |
| 523 | StoreInstanceFieldInstr::Kind |
| 524 | kind /* = StoreInstanceFieldInstr::Kind::kOther */) { |
| 525 | Fragment instructions; |
| 526 | const Field& field_clone = MayCloneField(field); |
| 527 | if (I->use_field_guards()) { |
| 528 | LocalVariable* store_expression = MakeTemporary(); |
| 529 | instructions += LoadLocal(store_expression); |
| 530 | instructions += GuardFieldClass(field_clone, GetNextDeoptId()); |
| 531 | |
| 532 | // Field length guard can be omitted if it is not needed. |
| 533 | // However, it is possible that we were tracking list length previously, |
| 534 | // and generated length guards in the past. We need to generate same IL |
| 535 | // to keep deopt ids stable, but we can discard generated IL fragment |
| 536 | // if length guard is not needed. |
| 537 | Fragment length_guard; |
| 538 | length_guard += LoadLocal(store_expression); |
| 539 | length_guard += GuardFieldLength(field_clone, GetNextDeoptId()); |
| 540 | |
| 541 | if (field_clone.needs_length_check()) { |
| 542 | instructions += length_guard; |
| 543 | } |
| 544 | |
| 545 | // If we are tracking exactness of the static type of the field then |
| 546 | // emit appropriate guard. |
| 547 | if (field_clone.static_type_exactness_state().IsTracking()) { |
| 548 | instructions += LoadLocal(store_expression); |
| 549 | instructions <<= |
| 550 | new (Z) GuardFieldTypeInstr(Pop(), field_clone, GetNextDeoptId()); |
| 551 | } |
| 552 | } |
| 553 | instructions += StoreInstanceField(field_clone, kind); |
| 554 | return instructions; |
| 555 | } |
| 556 | |
| 557 | Fragment BaseFlowGraphBuilder::LoadStaticField(const Field& field, |
| 558 | bool calls_initializer) { |
| 559 | LoadStaticFieldInstr* load = new (Z) LoadStaticFieldInstr( |
| 560 | field, TokenPosition::kNoSource, calls_initializer, |
| 561 | calls_initializer ? GetNextDeoptId() : DeoptId::kNone); |
| 562 | Push(load); |
| 563 | return Fragment(load); |
| 564 | } |
| 565 | |
| 566 | Fragment BaseFlowGraphBuilder::RedefinitionWithType(const AbstractType& type) { |
| 567 | auto redefinition = new (Z) RedefinitionInstr(Pop()); |
| 568 | redefinition->set_constrained_type( |
| 569 | new (Z) CompileType(CompileType::FromAbstractType(type))); |
| 570 | Push(redefinition); |
| 571 | return Fragment(redefinition); |
| 572 | } |
| 573 | |
| 574 | Fragment BaseFlowGraphBuilder::ReachabilityFence() { |
| 575 | Fragment instructions; |
| 576 | instructions <<= new (Z) ReachabilityFenceInstr(Pop()); |
| 577 | return instructions; |
| 578 | } |
| 579 | |
| 580 | Fragment BaseFlowGraphBuilder::Utf8Scan() { |
| 581 | Value* table = Pop(); |
| 582 | Value* end = Pop(); |
| 583 | Value* start = Pop(); |
| 584 | Value* bytes = Pop(); |
| 585 | Value* decoder = Pop(); |
| 586 | const Field& scan_flags_field = |
| 587 | compiler::LookupConvertUtf8DecoderScanFlagsField(); |
| 588 | auto scan = new (Z) Utf8ScanInstr( |
| 589 | decoder, bytes, start, end, table, |
| 590 | Slot::Get(MayCloneField(scan_flags_field), parsed_function_)); |
| 591 | Push(scan); |
| 592 | return Fragment(scan); |
| 593 | } |
| 594 | |
| 595 | Fragment BaseFlowGraphBuilder::StoreStaticField(TokenPosition position, |
| 596 | const Field& field) { |
| 597 | return Fragment( |
| 598 | new (Z) StoreStaticFieldInstr(MayCloneField(field), Pop(), position)); |
| 599 | } |
| 600 | |
| 601 | Fragment BaseFlowGraphBuilder::StoreIndexed(classid_t class_id) { |
| 602 | Value* value = Pop(); |
| 603 | Value* index = Pop(); |
| 604 | const StoreBarrierType emit_store_barrier = |
| 605 | value->BindsToConstant() ? kNoStoreBarrier : kEmitStoreBarrier; |
| 606 | StoreIndexedInstr* store = new (Z) StoreIndexedInstr( |
| 607 | Pop(), // Array. |
| 608 | index, value, emit_store_barrier, /*index_unboxed=*/false, |
| 609 | |
| 610 | compiler::target::Instance::ElementSizeFor(class_id), class_id, |
| 611 | kAlignedAccess, DeoptId::kNone, TokenPosition::kNoSource); |
| 612 | return Fragment(store); |
| 613 | } |
| 614 | |
| 615 | Fragment BaseFlowGraphBuilder::StoreIndexedTypedData(classid_t class_id, |
| 616 | intptr_t index_scale, |
| 617 | bool index_unboxed) { |
| 618 | // We use C behavior when dereferencing pointers, we assume alignment. |
| 619 | const AlignmentType alignment = kAlignedAccess; |
| 620 | |
| 621 | Value* value = Pop(); |
| 622 | Value* index = Pop(); |
| 623 | Value* c_pointer = Pop(); |
| 624 | StoreIndexedInstr* instr = new (Z) StoreIndexedInstr( |
| 625 | c_pointer, index, value, kNoStoreBarrier, index_unboxed, index_scale, |
| 626 | class_id, alignment, DeoptId::kNone, TokenPosition::kNoSource, |
| 627 | Instruction::SpeculativeMode::kNotSpeculative); |
| 628 | return Fragment(instr); |
| 629 | } |
| 630 | |
| 631 | Fragment BaseFlowGraphBuilder::StoreLocal(TokenPosition position, |
| 632 | LocalVariable* variable) { |
| 633 | if (variable->is_captured()) { |
| 634 | Fragment instructions; |
| 635 | LocalVariable* value = MakeTemporary(); |
| 636 | instructions += LoadContextAt(variable->owner()->context_level()); |
| 637 | instructions += LoadLocal(value); |
| 638 | instructions += StoreInstanceField( |
| 639 | position, Slot::GetContextVariableSlotFor(thread_, *variable)); |
| 640 | return instructions; |
| 641 | } |
| 642 | return StoreLocalRaw(position, variable); |
| 643 | } |
| 644 | |
| 645 | Fragment BaseFlowGraphBuilder::StoreLocalRaw(TokenPosition position, |
| 646 | LocalVariable* variable) { |
| 647 | ASSERT(!variable->is_captured()); |
| 648 | Value* value = Pop(); |
| 649 | StoreLocalInstr* store = new (Z) StoreLocalInstr(*variable, value, position); |
| 650 | Fragment instructions(store); |
| 651 | Push(store); |
| 652 | return instructions; |
| 653 | } |
| 654 | |
| 655 | LocalVariable* BaseFlowGraphBuilder::MakeTemporary() { |
| 656 | char name[64]; |
| 657 | intptr_t index = stack_->definition()->temp_index(); |
| 658 | Utils::SNPrint(name, 64, ":t%" Pd, index); |
| 659 | const String& symbol_name = |
| 660 | String::ZoneHandle(Z, Symbols::New(thread_, name)); |
| 661 | LocalVariable* variable = |
| 662 | new (Z) LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource, |
| 663 | symbol_name, Object::dynamic_type()); |
| 664 | // Set the index relative to the base of the expression stack including |
| 665 | // outgoing arguments. |
| 666 | variable->set_index( |
| 667 | VariableIndex(-parsed_function_->num_stack_locals() - index)); |
| 668 | |
| 669 | // The value on top of the stack has uses as if it were a local variable. |
| 670 | // Mark all definitions on the stack as used so that their temp indices |
| 671 | // will not be cleared (causing them to never be materialized in the |
| 672 | // expression stack and skew stack depth). |
| 673 | for (Value* item = stack_; item != nullptr; item = item->next_use()) { |
| 674 | item->definition()->set_ssa_temp_index(0); |
| 675 | } |
| 676 | |
| 677 | return variable; |
| 678 | } |
| 679 | |
| 680 | void BaseFlowGraphBuilder::SetTempIndex(Definition* definition) { |
| 681 | definition->set_temp_index( |
| 682 | stack_ == NULL ? 0 : stack_->definition()->temp_index() + 1); |
| 683 | } |
| 684 | |
| 685 | void BaseFlowGraphBuilder::Push(Definition* definition) { |
| 686 | SetTempIndex(definition); |
| 687 | Value::AddToList(new (Z) Value(definition), &stack_); |
| 688 | } |
| 689 | |
| 690 | Definition* BaseFlowGraphBuilder::Peek(intptr_t depth) { |
| 691 | Value* head = stack_; |
| 692 | for (intptr_t i = 0; i < depth; ++i) { |
| 693 | ASSERT(head != nullptr); |
| 694 | head = head->next_use(); |
| 695 | } |
| 696 | ASSERT(head != nullptr); |
| 697 | return head->definition(); |
| 698 | } |
| 699 | |
| 700 | Value* BaseFlowGraphBuilder::Pop() { |
| 701 | ASSERT(stack_ != NULL); |
| 702 | Value* value = stack_; |
| 703 | stack_ = value->next_use(); |
| 704 | if (stack_ != NULL) stack_->set_previous_use(NULL); |
| 705 | |
| 706 | value->set_next_use(NULL); |
| 707 | value->set_previous_use(NULL); |
| 708 | value->definition()->ClearSSATempIndex(); |
| 709 | return value; |
| 710 | } |
| 711 | |
| 712 | Fragment BaseFlowGraphBuilder::Drop() { |
| 713 | ASSERT(stack_ != NULL); |
| 714 | Fragment instructions; |
| 715 | Definition* definition = stack_->definition(); |
| 716 | // The SSA renaming implementation doesn't like [LoadLocal]s without a |
| 717 | // tempindex. |
| 718 | if (definition->HasSSATemp() || definition->IsLoadLocal()) { |
| 719 | instructions <<= new (Z) DropTempsInstr(1, NULL); |
| 720 | } else { |
| 721 | definition->ClearTempIndex(); |
| 722 | } |
| 723 | |
| 724 | Pop(); |
| 725 | return instructions; |
| 726 | } |
| 727 | |
| 728 | Fragment BaseFlowGraphBuilder::DropTempsPreserveTop( |
| 729 | intptr_t num_temps_to_drop) { |
| 730 | Value* top = Pop(); |
| 731 | |
| 732 | for (intptr_t i = 0; i < num_temps_to_drop; ++i) { |
| 733 | Pop(); |
| 734 | } |
| 735 | |
| 736 | DropTempsInstr* drop_temps = new (Z) DropTempsInstr(num_temps_to_drop, top); |
| 737 | Push(drop_temps); |
| 738 | |
| 739 | return Fragment(drop_temps); |
| 740 | } |
| 741 | |
| 742 | Fragment BaseFlowGraphBuilder::MakeTemp() { |
| 743 | MakeTempInstr* make_temp = new (Z) MakeTempInstr(Z); |
| 744 | Push(make_temp); |
| 745 | return Fragment(make_temp); |
| 746 | } |
| 747 | |
| 748 | TargetEntryInstr* BaseFlowGraphBuilder::BuildTargetEntry() { |
| 749 | return new (Z) TargetEntryInstr(AllocateBlockId(), CurrentTryIndex(), |
| 750 | GetNextDeoptId(), GetStackDepth()); |
| 751 | } |
| 752 | |
| 753 | FunctionEntryInstr* BaseFlowGraphBuilder::BuildFunctionEntry( |
| 754 | GraphEntryInstr* graph_entry) { |
| 755 | return new (Z) FunctionEntryInstr(graph_entry, AllocateBlockId(), |
| 756 | CurrentTryIndex(), GetNextDeoptId()); |
| 757 | } |
| 758 | |
| 759 | JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) { |
| 760 | return new (Z) JoinEntryInstr(AllocateBlockId(), try_index, GetNextDeoptId(), |
| 761 | GetStackDepth()); |
| 762 | } |
| 763 | |
| 764 | JoinEntryInstr* BaseFlowGraphBuilder::BuildJoinEntry() { |
| 765 | return new (Z) JoinEntryInstr(AllocateBlockId(), CurrentTryIndex(), |
| 766 | GetNextDeoptId(), GetStackDepth()); |
| 767 | } |
| 768 | |
| 769 | IndirectEntryInstr* BaseFlowGraphBuilder::BuildIndirectEntry( |
| 770 | intptr_t indirect_id, |
| 771 | intptr_t try_index) { |
| 772 | return new (Z) IndirectEntryInstr(AllocateBlockId(), indirect_id, try_index, |
| 773 | GetNextDeoptId()); |
| 774 | } |
| 775 | |
| 776 | InputsArray* BaseFlowGraphBuilder::GetArguments(int count) { |
| 777 | InputsArray* arguments = new (Z) ZoneGrowableArray<Value*>(Z, count); |
| 778 | arguments->SetLength(count); |
| 779 | for (intptr_t i = count - 1; i >= 0; --i) { |
| 780 | arguments->data()[i] = Pop(); |
| 781 | } |
| 782 | return arguments; |
| 783 | } |
| 784 | |
| 785 | Fragment BaseFlowGraphBuilder::SmiRelationalOp(Token::Kind kind) { |
| 786 | Value* right = Pop(); |
| 787 | Value* left = Pop(); |
| 788 | RelationalOpInstr* instr = new (Z) RelationalOpInstr( |
| 789 | TokenPosition::kNoSource, kind, left, right, kSmiCid, GetNextDeoptId()); |
| 790 | Push(instr); |
| 791 | return Fragment(instr); |
| 792 | } |
| 793 | |
| 794 | Fragment BaseFlowGraphBuilder::SmiBinaryOp(Token::Kind kind, |
| 795 | bool is_truncating) { |
| 796 | Value* right = Pop(); |
| 797 | Value* left = Pop(); |
| 798 | BinarySmiOpInstr* instr = |
| 799 | new (Z) BinarySmiOpInstr(kind, left, right, GetNextDeoptId()); |
| 800 | if (is_truncating) { |
| 801 | instr->mark_truncating(); |
| 802 | } |
| 803 | Push(instr); |
| 804 | return Fragment(instr); |
| 805 | } |
| 806 | |
| 807 | Fragment BaseFlowGraphBuilder::BinaryIntegerOp(Token::Kind kind, |
| 808 | Representation representation, |
| 809 | bool is_truncating) { |
| 810 | ASSERT(representation == kUnboxedInt32 || representation == kUnboxedUint32 || |
| 811 | representation == kUnboxedInt64); |
| 812 | Value* right = Pop(); |
| 813 | Value* left = Pop(); |
| 814 | BinaryIntegerOpInstr* instr; |
| 815 | switch (representation) { |
| 816 | case kUnboxedInt32: |
| 817 | instr = new (Z) BinaryInt32OpInstr(kind, left, right, GetNextDeoptId()); |
| 818 | break; |
| 819 | case kUnboxedUint32: |
| 820 | instr = new (Z) BinaryUint32OpInstr(kind, left, right, GetNextDeoptId()); |
| 821 | break; |
| 822 | case kUnboxedInt64: |
| 823 | instr = new (Z) BinaryInt64OpInstr(kind, left, right, GetNextDeoptId()); |
| 824 | break; |
| 825 | default: |
| 826 | UNREACHABLE(); |
| 827 | } |
| 828 | if (is_truncating) { |
| 829 | instr->mark_truncating(); |
| 830 | } |
| 831 | Push(instr); |
| 832 | return Fragment(instr); |
| 833 | } |
| 834 | |
| 835 | Fragment BaseFlowGraphBuilder::LoadFpRelativeSlot( |
| 836 | intptr_t offset, |
| 837 | CompileType result_type, |
| 838 | Representation representation) { |
| 839 | LoadIndexedUnsafeInstr* instr = new (Z) |
| 840 | LoadIndexedUnsafeInstr(Pop(), offset, result_type, representation); |
| 841 | Push(instr); |
| 842 | return Fragment(instr); |
| 843 | } |
| 844 | |
| 845 | Fragment BaseFlowGraphBuilder::StoreFpRelativeSlot(intptr_t offset) { |
| 846 | Value* value = Pop(); |
| 847 | Value* index = Pop(); |
| 848 | StoreIndexedUnsafeInstr* instr = |
| 849 | new (Z) StoreIndexedUnsafeInstr(index, value, offset); |
| 850 | return Fragment(instr); |
| 851 | } |
| 852 | |
| 853 | JoinEntryInstr* BaseFlowGraphBuilder::BuildThrowNoSuchMethod() { |
| 854 | JoinEntryInstr* nsm = BuildJoinEntry(); |
| 855 | |
| 856 | Fragment failing(nsm); |
| 857 | const Code& nsm_handler = Code::ZoneHandle( |
| 858 | Z, I->object_store()->call_closure_no_such_method_stub()); |
| 859 | failing += LoadArgDescriptor(); |
| 860 | failing += TailCall(nsm_handler); |
| 861 | |
| 862 | return nsm; |
| 863 | } |
| 864 | |
| 865 | Fragment BaseFlowGraphBuilder::AssertBool(TokenPosition position) { |
| 866 | Value* value = Pop(); |
| 867 | AssertBooleanInstr* instr = |
| 868 | new (Z) AssertBooleanInstr(position, value, GetNextDeoptId()); |
| 869 | Push(instr); |
| 870 | return Fragment(instr); |
| 871 | } |
| 872 | |
| 873 | Fragment BaseFlowGraphBuilder::BooleanNegate() { |
| 874 | BooleanNegateInstr* negate = new (Z) BooleanNegateInstr(Pop()); |
| 875 | Push(negate); |
| 876 | return Fragment(negate); |
| 877 | } |
| 878 | |
| 879 | Fragment BaseFlowGraphBuilder::AllocateContext( |
| 880 | const ZoneGrowableArray<const Slot*>& context_slots) { |
| 881 | AllocateContextInstr* allocate = |
| 882 | new (Z) AllocateContextInstr(TokenPosition::kNoSource, context_slots); |
| 883 | Push(allocate); |
| 884 | return Fragment(allocate); |
| 885 | } |
| 886 | |
| 887 | Fragment BaseFlowGraphBuilder::AllocateClosure( |
| 888 | TokenPosition position, |
| 889 | const Function& closure_function) { |
| 890 | const Class& cls = Class::ZoneHandle(Z, I->object_store()->closure_class()); |
| 891 | AllocateObjectInstr* allocate = new (Z) AllocateObjectInstr(position, cls); |
| 892 | allocate->set_closure_function(closure_function); |
| 893 | Push(allocate); |
| 894 | return Fragment(allocate); |
| 895 | } |
| 896 | |
| 897 | Fragment BaseFlowGraphBuilder::CreateArray() { |
| 898 | Value* element_count = Pop(); |
| 899 | CreateArrayInstr* array = |
| 900 | new (Z) CreateArrayInstr(TokenPosition::kNoSource, |
| 901 | Pop(), // Element type. |
| 902 | element_count, GetNextDeoptId()); |
| 903 | Push(array); |
| 904 | return Fragment(array); |
| 905 | } |
| 906 | |
| 907 | Fragment BaseFlowGraphBuilder::InstantiateType(const AbstractType& type) { |
| 908 | Value* function_type_args = Pop(); |
| 909 | Value* instantiator_type_args = Pop(); |
| 910 | InstantiateTypeInstr* instr = new (Z) InstantiateTypeInstr( |
| 911 | TokenPosition::kNoSource, type, instantiator_type_args, |
| 912 | function_type_args, GetNextDeoptId()); |
| 913 | Push(instr); |
| 914 | return Fragment(instr); |
| 915 | } |
| 916 | |
| 917 | Fragment BaseFlowGraphBuilder::InstantiateTypeArguments( |
| 918 | const TypeArguments& type_arguments) { |
| 919 | Value* function_type_args = Pop(); |
| 920 | Value* instantiator_type_args = Pop(); |
| 921 | const Class& instantiator_class = Class::ZoneHandle(Z, function_.Owner()); |
| 922 | InstantiateTypeArgumentsInstr* instr = new (Z) InstantiateTypeArgumentsInstr( |
| 923 | TokenPosition::kNoSource, type_arguments, instantiator_class, function_, |
| 924 | instantiator_type_args, function_type_args, GetNextDeoptId()); |
| 925 | Push(instr); |
| 926 | return Fragment(instr); |
| 927 | } |
| 928 | |
| 929 | Fragment BaseFlowGraphBuilder::LoadClassId() { |
| 930 | LoadClassIdInstr* load = new (Z) LoadClassIdInstr(Pop()); |
| 931 | Push(load); |
| 932 | return Fragment(load); |
| 933 | } |
| 934 | |
| 935 | Fragment BaseFlowGraphBuilder::AllocateObject(TokenPosition position, |
| 936 | const Class& klass, |
| 937 | intptr_t argument_count) { |
| 938 | ASSERT((argument_count == 0) || (argument_count == 1)); |
| 939 | Value* type_arguments = (argument_count > 0) ? Pop() : nullptr; |
| 940 | AllocateObjectInstr* allocate = |
| 941 | new (Z) AllocateObjectInstr(position, klass, type_arguments); |
| 942 | Push(allocate); |
| 943 | return Fragment(allocate); |
| 944 | } |
| 945 | |
| 946 | Fragment BaseFlowGraphBuilder::Box(Representation from) { |
| 947 | BoxInstr* box = BoxInstr::Create(from, Pop()); |
| 948 | Push(box); |
| 949 | return Fragment(box); |
| 950 | } |
| 951 | |
| 952 | Fragment BaseFlowGraphBuilder::BuildFfiAsFunctionInternalCall( |
| 953 | const TypeArguments& signatures) { |
| 954 | ASSERT(signatures.IsInstantiated()); |
| 955 | ASSERT(signatures.Length() == 2); |
| 956 | |
| 957 | const AbstractType& dart_type = AbstractType::Handle(signatures.TypeAt(0)); |
| 958 | const AbstractType& native_type = AbstractType::Handle(signatures.TypeAt(1)); |
| 959 | |
| 960 | ASSERT(dart_type.IsFunctionType() && native_type.IsFunctionType()); |
| 961 | const Function& target = |
| 962 | Function::ZoneHandle(compiler::ffi::TrampolineFunction( |
| 963 | Function::Handle(Z, Type::Cast(dart_type).signature()), |
| 964 | Function::Handle(Z, Type::Cast(native_type).signature()))); |
| 965 | |
| 966 | Fragment code; |
| 967 | // Store the pointer in the context, we cannot load the untagged address |
| 968 | // here as these can be unoptimized call sites. |
| 969 | LocalVariable* pointer = MakeTemporary(); |
| 970 | |
| 971 | auto& context_slots = CompilerState::Current().GetDummyContextSlots( |
| 972 | /*context_id=*/0, /*num_variables=*/1); |
| 973 | code += AllocateContext(context_slots); |
| 974 | LocalVariable* context = MakeTemporary(); |
| 975 | |
| 976 | code += LoadLocal(context); |
| 977 | code += LoadLocal(pointer); |
| 978 | code += StoreInstanceField(TokenPosition::kNoSource, *context_slots[0]); |
| 979 | |
| 980 | code += AllocateClosure(TokenPosition::kNoSource, target); |
| 981 | LocalVariable* closure = MakeTemporary(); |
| 982 | |
| 983 | code += LoadLocal(closure); |
| 984 | code += LoadLocal(context); |
| 985 | code += StoreInstanceField(TokenPosition::kNoSource, Slot::Closure_context(), |
| 986 | StoreInstanceFieldInstr::Kind::kInitializing); |
| 987 | |
| 988 | code += LoadLocal(closure); |
| 989 | code += Constant(target); |
| 990 | code += StoreInstanceField(TokenPosition::kNoSource, Slot::Closure_function(), |
| 991 | StoreInstanceFieldInstr::Kind::kInitializing); |
| 992 | |
| 993 | // Drop address and context. |
| 994 | code += DropTempsPreserveTop(2); |
| 995 | |
| 996 | return code; |
| 997 | } |
| 998 | |
| 999 | Fragment BaseFlowGraphBuilder::DebugStepCheck(TokenPosition position) { |
| 1000 | #ifdef PRODUCT |
| 1001 | return Fragment(); |
| 1002 | #else |
| 1003 | return Fragment(new (Z) DebugStepCheckInstr( |
| 1004 | position, PcDescriptorsLayout::kRuntimeCall, GetNextDeoptId())); |
| 1005 | #endif |
| 1006 | } |
| 1007 | |
| 1008 | Fragment BaseFlowGraphBuilder::CheckNull(TokenPosition position, |
| 1009 | LocalVariable* receiver, |
| 1010 | const String& function_name, |
| 1011 | bool clear_the_temp /* = true */) { |
| 1012 | Fragment instructions = LoadLocal(receiver); |
| 1013 | |
| 1014 | CheckNullInstr* check_null = new (Z) |
| 1015 | CheckNullInstr(Pop(), function_name, GetNextDeoptId(), position, |
| 1016 | function_name.IsNull() ? CheckNullInstr::kCastError |
| 1017 | : CheckNullInstr::kNoSuchMethod); |
| 1018 | |
| 1019 | // Does not use the redefinition, no `Push(check_null)`. |
| 1020 | instructions <<= check_null; |
| 1021 | |
| 1022 | if (clear_the_temp) { |
| 1023 | // Null out receiver to make sure it is not saved into the frame before |
| 1024 | // doing the call. |
| 1025 | instructions += NullConstant(); |
| 1026 | instructions += StoreLocal(TokenPosition::kNoSource, receiver); |
| 1027 | instructions += Drop(); |
| 1028 | } |
| 1029 | |
| 1030 | return instructions; |
| 1031 | } |
| 1032 | |
| 1033 | Fragment BaseFlowGraphBuilder::CheckNullOptimized(TokenPosition position, |
| 1034 | const String& function_name) { |
| 1035 | Value* value = Pop(); |
| 1036 | CheckNullInstr* check_null = |
| 1037 | new (Z) CheckNullInstr(value, function_name, GetNextDeoptId(), position); |
| 1038 | Push(check_null); // Use the redefinition. |
| 1039 | return Fragment(check_null); |
| 1040 | } |
| 1041 | |
| 1042 | void BaseFlowGraphBuilder::RecordUncheckedEntryPoint( |
| 1043 | GraphEntryInstr* graph_entry, |
| 1044 | FunctionEntryInstr* unchecked_entry) { |
| 1045 | // Closures always check all arguments on their checked entry-point, most |
| 1046 | // call-sites are unchecked, and they're inlined less often, so it's very |
| 1047 | // beneficial to build multiple entry-points for them. Regular methods however |
| 1048 | // have fewer checks to begin with since they have dynamic invocation |
| 1049 | // forwarders, so in AOT we implement a more conservative time-space tradeoff |
| 1050 | // by only building the unchecked entry-point when inlining. We should |
| 1051 | // reconsider this heuristic if we identify non-inlined type-checks in |
| 1052 | // hotspots of new benchmarks. |
| 1053 | if (!IsInlining() && (parsed_function_->function().IsClosureFunction() || |
| 1054 | !CompilerState::Current().is_aot())) { |
| 1055 | graph_entry->set_unchecked_entry(unchecked_entry); |
| 1056 | } else if (InliningUncheckedEntry()) { |
| 1057 | graph_entry->set_normal_entry(unchecked_entry); |
| 1058 | } |
| 1059 | } |
| 1060 | |
| 1061 | Fragment BaseFlowGraphBuilder::BuildEntryPointsIntrospection() { |
| 1062 | if (!FLAG_enable_testing_pragmas) return Drop(); |
| 1063 | |
| 1064 | auto& function = Function::Handle(Z, parsed_function_->function().raw()); |
| 1065 | |
| 1066 | if (function.IsImplicitClosureFunction()) { |
| 1067 | const auto& parent = Function::Handle(Z, function.parent_function()); |
| 1068 | const auto& func_name = String::Handle(Z, parent.name()); |
| 1069 | const auto& owner = Class::Handle(Z, parent.Owner()); |
| 1070 | function = owner.LookupFunction(func_name); |
| 1071 | } |
| 1072 | |
| 1073 | Object& options = Object::Handle(Z); |
| 1074 | if (!Library::FindPragma(thread_, /*only_core=*/false, function, |
| 1075 | Symbols::vm_trace_entrypoints(), &options) || |
| 1076 | options.IsNull() || !options.IsClosure()) { |
| 1077 | return Drop(); |
| 1078 | } |
| 1079 | auto& closure = Closure::ZoneHandle(Z, Closure::Cast(options).raw()); |
| 1080 | LocalVariable* entry_point_num = MakeTemporary(); |
| 1081 | |
| 1082 | auto& function_name = String::ZoneHandle( |
| 1083 | Z, String::New(function.ToLibNamePrefixedQualifiedCString(), Heap::kOld)); |
| 1084 | if (parsed_function_->function().IsImplicitClosureFunction()) { |
| 1085 | function_name = String::Concat( |
| 1086 | function_name, String::Handle(Z, String::New("#tearoff" , Heap::kNew)), |
| 1087 | Heap::kOld); |
| 1088 | } |
| 1089 | if (!function_name.IsCanonical()) { |
| 1090 | function_name = Symbols::New(thread_, function_name); |
| 1091 | } |
| 1092 | |
| 1093 | Fragment call_hook; |
| 1094 | call_hook += Constant(closure); |
| 1095 | call_hook += Constant(function_name); |
| 1096 | call_hook += LoadLocal(entry_point_num); |
| 1097 | call_hook += Constant(Function::ZoneHandle(Z, closure.function())); |
| 1098 | call_hook += ClosureCall(TokenPosition::kNoSource, |
| 1099 | /*type_args_len=*/0, /*argument_count=*/3, |
| 1100 | /*argument_names=*/Array::ZoneHandle(Z)); |
| 1101 | call_hook += Drop(); // result of closure call |
| 1102 | call_hook += Drop(); // entrypoint number |
| 1103 | return call_hook; |
| 1104 | } |
| 1105 | |
| 1106 | Fragment BaseFlowGraphBuilder::ClosureCall(TokenPosition position, |
| 1107 | intptr_t type_args_len, |
| 1108 | intptr_t argument_count, |
| 1109 | const Array& argument_names, |
| 1110 | bool is_statically_checked) { |
| 1111 | const intptr_t total_count = argument_count + (type_args_len > 0 ? 1 : 0) + 1; |
| 1112 | InputsArray* arguments = GetArguments(total_count); |
| 1113 | ClosureCallInstr* call = new (Z) ClosureCallInstr( |
| 1114 | arguments, type_args_len, argument_names, position, GetNextDeoptId(), |
| 1115 | is_statically_checked ? Code::EntryKind::kUnchecked |
| 1116 | : Code::EntryKind::kNormal); |
| 1117 | Push(call); |
| 1118 | return Fragment(call); |
| 1119 | } |
| 1120 | |
| 1121 | Fragment BaseFlowGraphBuilder::StringInterpolate(TokenPosition position) { |
| 1122 | Value* array = Pop(); |
| 1123 | StringInterpolateInstr* interpolate = |
| 1124 | new (Z) StringInterpolateInstr(array, position, GetNextDeoptId()); |
| 1125 | Push(interpolate); |
| 1126 | return Fragment(interpolate); |
| 1127 | } |
| 1128 | |
| 1129 | void BaseFlowGraphBuilder::reset_context_depth_for_deopt_id(intptr_t deopt_id) { |
| 1130 | if (is_recording_context_levels()) { |
| 1131 | for (intptr_t i = 0, n = context_level_array_->length(); i < n; i += 2) { |
| 1132 | if (context_level_array_->At(i) == deopt_id) { |
| 1133 | (*context_level_array_)[i + 1] = context_depth_; |
| 1134 | return; |
| 1135 | } |
| 1136 | ASSERT(context_level_array_->At(i) < deopt_id); |
| 1137 | } |
| 1138 | } |
| 1139 | } |
| 1140 | |
| 1141 | Fragment BaseFlowGraphBuilder::AssertAssignable( |
| 1142 | TokenPosition position, |
| 1143 | const String& dst_name, |
| 1144 | AssertAssignableInstr::Kind kind) { |
| 1145 | Value* function_type_args = Pop(); |
| 1146 | Value* instantiator_type_args = Pop(); |
| 1147 | Value* dst_type = Pop(); |
| 1148 | Value* value = Pop(); |
| 1149 | |
| 1150 | AssertAssignableInstr* instr = new (Z) AssertAssignableInstr( |
| 1151 | position, value, dst_type, instantiator_type_args, function_type_args, |
| 1152 | dst_name, GetNextDeoptId(), kind); |
| 1153 | Push(instr); |
| 1154 | |
| 1155 | return Fragment(instr); |
| 1156 | } |
| 1157 | |
| 1158 | Fragment BaseFlowGraphBuilder::InitConstantParameters() { |
| 1159 | Fragment instructions; |
| 1160 | const intptr_t parameter_count = parsed_function_->function().NumParameters(); |
| 1161 | for (intptr_t i = 0; i < parameter_count; ++i) { |
| 1162 | LocalVariable* raw_parameter = parsed_function_->RawParameterVariable(i); |
| 1163 | const Object* param_value = raw_parameter->parameter_value(); |
| 1164 | if (param_value != nullptr) { |
| 1165 | instructions += Constant(*param_value); |
| 1166 | instructions += StoreLocalRaw(TokenPosition::kNoSource, raw_parameter); |
| 1167 | instructions += Drop(); |
| 1168 | } |
| 1169 | } |
| 1170 | return instructions; |
| 1171 | } |
| 1172 | |
| 1173 | } // namespace kernel |
| 1174 | } // namespace dart |
| 1175 | |