Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(519)

Side by Side Diff: src/hydrogen.cc

Issue 18496002: Factor out OSR-related graph-building functionality from hydrogen.cc. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Extract HOsrBuilder declaration to its own header file. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-osr.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 19 matching lines...) Expand all
30 #include <algorithm> 30 #include <algorithm>
31 31
32 #include "v8.h" 32 #include "v8.h"
33 #include "codegen.h" 33 #include "codegen.h"
34 #include "full-codegen.h" 34 #include "full-codegen.h"
35 #include "hashmap.h" 35 #include "hashmap.h"
36 #include "hydrogen-environment-liveness.h" 36 #include "hydrogen-environment-liveness.h"
37 #include "hydrogen-escape-analysis.h" 37 #include "hydrogen-escape-analysis.h"
38 #include "hydrogen-infer-representation.h" 38 #include "hydrogen-infer-representation.h"
39 #include "hydrogen-gvn.h" 39 #include "hydrogen-gvn.h"
40 #include "hydrogen-osr.h"
40 #include "hydrogen-uint32-analysis.h" 41 #include "hydrogen-uint32-analysis.h"
41 #include "lithium-allocator.h" 42 #include "lithium-allocator.h"
42 #include "parser.h" 43 #include "parser.h"
43 #include "scopeinfo.h" 44 #include "scopeinfo.h"
44 #include "scopes.h" 45 #include "scopes.h"
45 #include "stub-cache.h" 46 #include "stub-cache.h"
46 #include "typing.h" 47 #include "typing.h"
47 48
48 #if V8_TARGET_ARCH_IA32 49 #if V8_TARGET_ARCH_IA32
49 #include "ia32/lithium-codegen-ia32.h" 50 #include "ia32/lithium-codegen-ia32.h"
(...skipping 1858 matching lines...) Expand 10 before | Expand all | Expand 10 after
1908 1909
1909 1910
1910 HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info) 1911 HOptimizedGraphBuilder::HOptimizedGraphBuilder(CompilationInfo* info)
1911 : HGraphBuilder(info), 1912 : HGraphBuilder(info),
1912 function_state_(NULL), 1913 function_state_(NULL),
1913 initial_function_state_(this, info, NORMAL_RETURN), 1914 initial_function_state_(this, info, NORMAL_RETURN),
1914 ast_context_(NULL), 1915 ast_context_(NULL),
1915 break_scope_(NULL), 1916 break_scope_(NULL),
1916 inlined_count_(0), 1917 inlined_count_(0),
1917 globals_(10, info->zone()), 1918 globals_(10, info->zone()),
1918 inline_bailout_(false) { 1919 inline_bailout_(false),
1920 osr_(new(info->zone()) HOsrBuilder(this)) {
1919 // This is not initialized in the initializer list because the 1921 // This is not initialized in the initializer list because the
1920 // constructor for the initial state relies on function_state_ == NULL 1922 // constructor for the initial state relies on function_state_ == NULL
1921 // to know it's the initial state. 1923 // to know it's the initial state.
1922 function_state_= &initial_function_state_; 1924 function_state_= &initial_function_state_;
1923 InitializeAstVisitor(); 1925 InitializeAstVisitor();
1924 } 1926 }
1925 1927
1926 1928
1927 HBasicBlock* HOptimizedGraphBuilder::CreateJoin(HBasicBlock* first, 1929 HBasicBlock* HOptimizedGraphBuilder::CreateJoin(HBasicBlock* first,
1928 HBasicBlock* second, 1930 HBasicBlock* second,
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1976 1978
1977 1979
1978 HGraph::HGraph(CompilationInfo* info) 1980 HGraph::HGraph(CompilationInfo* info)
1979 : isolate_(info->isolate()), 1981 : isolate_(info->isolate()),
1980 next_block_id_(0), 1982 next_block_id_(0),
1981 entry_block_(NULL), 1983 entry_block_(NULL),
1982 blocks_(8, info->zone()), 1984 blocks_(8, info->zone()),
1983 values_(16, info->zone()), 1985 values_(16, info->zone()),
1984 phi_list_(NULL), 1986 phi_list_(NULL),
1985 uint32_instructions_(NULL), 1987 uint32_instructions_(NULL),
1988 osr_(NULL),
1986 info_(info), 1989 info_(info),
1987 zone_(info->zone()), 1990 zone_(info->zone()),
1988 is_recursive_(false), 1991 is_recursive_(false),
1989 use_optimistic_licm_(false), 1992 use_optimistic_licm_(false),
1990 has_soft_deoptimize_(false), 1993 has_soft_deoptimize_(false),
1991 depends_on_empty_array_proto_elements_(false), 1994 depends_on_empty_array_proto_elements_(false),
1992 type_change_checksum_(0), 1995 type_change_checksum_(0),
1993 maximum_environment_size_(0) { 1996 maximum_environment_size_(0) {
1994 if (info->IsStub()) { 1997 if (info->IsStub()) {
1995 HydrogenCodeStub* stub = info->code_stub(); 1998 HydrogenCodeStub* stub = info->code_stub();
(...skipping 1529 matching lines...) Expand 10 before | Expand all | Expand 10 after
3525 Handle<Code> unoptimized_code(current_info()->shared_info()->code()); 3528 Handle<Code> unoptimized_code(current_info()->shared_info()->code());
3526 ASSERT(unoptimized_code->kind() == Code::FUNCTION); 3529 ASSERT(unoptimized_code->kind() == Code::FUNCTION);
3527 Handle<TypeFeedbackInfo> type_info( 3530 Handle<TypeFeedbackInfo> type_info(
3528 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info())); 3531 TypeFeedbackInfo::cast(unoptimized_code->type_feedback_info()));
3529 int checksum = type_info->own_type_change_checksum(); 3532 int checksum = type_info->own_type_change_checksum();
3530 int composite_checksum = graph()->update_type_change_checksum(checksum); 3533 int composite_checksum = graph()->update_type_change_checksum(checksum);
3531 graph()->set_use_optimistic_licm( 3534 graph()->set_use_optimistic_licm(
3532 !type_info->matches_inlined_type_change_checksum(composite_checksum)); 3535 !type_info->matches_inlined_type_change_checksum(composite_checksum));
3533 type_info->set_inlined_type_change_checksum(composite_checksum); 3536 type_info->set_inlined_type_change_checksum(composite_checksum);
3534 3537
3538 // Perform any necessary OSR-specific cleanups or changes to the graph.
3539 osr_->FinishGraph();
3540
3535 return true; 3541 return true;
3536 } 3542 }
3537 3543
3538 3544
3539 bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) { 3545 bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
3540 *bailout_reason = SmartArrayPointer<char>(); 3546 *bailout_reason = SmartArrayPointer<char>();
3541 OrderBlocks(); 3547 OrderBlocks();
3542 AssignDominators(); 3548 AssignDominators();
3543 3549
3544 // We need to create a HConstant "zero" now so that GVN will fold every 3550 // We need to create a HConstant "zero" now so that GVN will fold every
(...skipping 23 matching lines...) Expand all
3568 "Unsupported phi use of arguments")); 3574 "Unsupported phi use of arguments"));
3569 return false; 3575 return false;
3570 } 3576 }
3571 3577
3572 // Remove dead code and phis 3578 // Remove dead code and phis
3573 if (FLAG_dead_code_elimination) { 3579 if (FLAG_dead_code_elimination) {
3574 DeadCodeElimination("H_Eliminate early dead code"); 3580 DeadCodeElimination("H_Eliminate early dead code");
3575 } 3581 }
3576 CollectPhis(); 3582 CollectPhis();
3577 3583
3578 if (has_osr_loop_entry()) { 3584 if (has_osr()) osr()->FinishOsrValues();
3579 const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
3580 for (int j = 0; j < phis->length(); j++) {
3581 HPhi* phi = phis->at(j);
3582 osr_values()->at(phi->merged_index())->set_incoming_value(phi);
3583 }
3584 }
3585 3585
3586 Run<HInferRepresentationPhase>(); 3586 Run<HInferRepresentationPhase>();
3587 3587
3588 // Remove HSimulate instructions that have turned out not to be needed 3588 // Remove HSimulate instructions that have turned out not to be needed
3589 // after all by folding them into the following HSimulate. 3589 // after all by folding them into the following HSimulate.
3590 // This must happen after inferring representations. 3590 // This must happen after inferring representations.
3591 MergeRemovableSimulates(); 3591 MergeRemovableSimulates();
3592 3592
3593 MarkDeoptimizeOnUndefined(); 3593 MarkDeoptimizeOnUndefined();
3594 InsertRepresentationChanges(); 3594 InsertRepresentationChanges();
(...skipping 1087 matching lines...) Expand 10 before | Expand all | Expand 10 after
4682 stmt->ExitId())); 4682 stmt->ExitId()));
4683 } else { 4683 } else {
4684 if (fall_through_block != NULL) fall_through_block->Goto(break_block); 4684 if (fall_through_block != NULL) fall_through_block->Goto(break_block);
4685 if (last_block != NULL) last_block->Goto(break_block); 4685 if (last_block != NULL) last_block->Goto(break_block);
4686 break_block->SetJoinId(stmt->ExitId()); 4686 break_block->SetJoinId(stmt->ExitId());
4687 set_current_block(break_block); 4687 set_current_block(break_block);
4688 } 4688 }
4689 } 4689 }
4690 4690
4691 4691
4692 bool HOptimizedGraphBuilder::HasOsrEntryAt(IterationStatement* statement) {
4693 return statement->OsrEntryId() == current_info()->osr_ast_id();
4694 }
4695
4696
4697 bool HOptimizedGraphBuilder::PreProcessOsrEntry(IterationStatement* statement) {
4698 if (!HasOsrEntryAt(statement)) return false;
4699
4700 HBasicBlock* non_osr_entry = graph()->CreateBasicBlock();
4701 HBasicBlock* osr_entry = graph()->CreateBasicBlock();
4702 HValue* true_value = graph()->GetConstantTrue();
4703 HBranch* test = new(zone()) HBranch(true_value, non_osr_entry, osr_entry);
4704 current_block()->Finish(test);
4705
4706 HBasicBlock* loop_predecessor = graph()->CreateBasicBlock();
4707 non_osr_entry->Goto(loop_predecessor);
4708
4709 set_current_block(osr_entry);
4710 osr_entry->set_osr_entry();
4711 BailoutId osr_entry_id = statement->OsrEntryId();
4712 int first_expression_index = environment()->first_expression_index();
4713 int length = environment()->length();
4714 ZoneList<HUnknownOSRValue*>* osr_values =
4715 new(zone()) ZoneList<HUnknownOSRValue*>(length, zone());
4716
4717 for (int i = 0; i < first_expression_index; ++i) {
4718 HUnknownOSRValue* osr_value = Add<HUnknownOSRValue>();
4719 environment()->Bind(i, osr_value);
4720 osr_values->Add(osr_value, zone());
4721 }
4722
4723 if (first_expression_index != length) {
4724 environment()->Drop(length - first_expression_index);
4725 for (int i = first_expression_index; i < length; ++i) {
4726 HUnknownOSRValue* osr_value = Add<HUnknownOSRValue>();
4727 environment()->Push(osr_value);
4728 osr_values->Add(osr_value, zone());
4729 }
4730 }
4731
4732 graph()->set_osr_values(osr_values);
4733
4734 AddSimulate(osr_entry_id);
4735 Add<HOsrEntry>(osr_entry_id);
4736 HContext* context = Add<HContext>();
4737 environment()->BindContext(context);
4738 current_block()->Goto(loop_predecessor);
4739 loop_predecessor->SetJoinId(statement->EntryId());
4740 set_current_block(loop_predecessor);
4741 return true;
4742 }
4743
4744
4745 void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt, 4692 void HOptimizedGraphBuilder::VisitLoopBody(IterationStatement* stmt,
4746 HBasicBlock* loop_entry, 4693 HBasicBlock* loop_entry,
4747 BreakAndContinueInfo* break_info) { 4694 BreakAndContinueInfo* break_info) {
4748 BreakAndContinueScope push(break_info, this); 4695 BreakAndContinueScope push(break_info, this);
4749 AddSimulate(stmt->StackCheckId()); 4696 AddSimulate(stmt->StackCheckId());
4750 HValue* context = environment()->LookupContext(); 4697 HValue* context = environment()->LookupContext();
4751 HStackCheck* stack_check = Add<HStackCheck>( 4698 HStackCheck* stack_check = Add<HStackCheck>(
4752 context, HStackCheck::kBackwardsBranch); 4699 context, HStackCheck::kBackwardsBranch);
4753 ASSERT(loop_entry->IsLoopHeader()); 4700 ASSERT(loop_entry->IsLoopHeader());
4754 loop_entry->loop_information()->set_stack_check(stack_check); 4701 loop_entry->loop_information()->set_stack_check(stack_check);
4755 CHECK_BAILOUT(Visit(stmt->body())); 4702 CHECK_BAILOUT(Visit(stmt->body()));
4756 } 4703 }
4757 4704
4758 4705
4759 void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) { 4706 void HOptimizedGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
4760 ASSERT(!HasStackOverflow()); 4707 ASSERT(!HasStackOverflow());
4761 ASSERT(current_block() != NULL); 4708 ASSERT(current_block() != NULL);
4762 ASSERT(current_block()->HasPredecessor()); 4709 ASSERT(current_block()->HasPredecessor());
4763 ASSERT(current_block() != NULL); 4710 ASSERT(current_block() != NULL);
4764 bool osr_entry = PreProcessOsrEntry(stmt); 4711 HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
4765 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4766 current_block()->Goto(loop_entry);
4767 set_current_block(loop_entry);
4768 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4769 4712
4770 BreakAndContinueInfo break_info(stmt); 4713 BreakAndContinueInfo break_info(stmt);
4771 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info)); 4714 CHECK_BAILOUT(VisitLoopBody(stmt, loop_entry, &break_info));
4772 HBasicBlock* body_exit = 4715 HBasicBlock* body_exit =
4773 JoinContinue(stmt, current_block(), break_info.continue_block()); 4716 JoinContinue(stmt, current_block(), break_info.continue_block());
4774 HBasicBlock* loop_successor = NULL; 4717 HBasicBlock* loop_successor = NULL;
4775 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) { 4718 if (body_exit != NULL && !stmt->cond()->ToBooleanIsTrue()) {
4776 set_current_block(body_exit); 4719 set_current_block(body_exit);
4777 // The block for a true condition, the actual predecessor block of the 4720 // The block for a true condition, the actual predecessor block of the
4778 // back edge. 4721 // back edge.
(...skipping 18 matching lines...) Expand all
4797 break_info.break_block()); 4740 break_info.break_block());
4798 set_current_block(loop_exit); 4741 set_current_block(loop_exit);
4799 } 4742 }
4800 4743
4801 4744
4802 void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) { 4745 void HOptimizedGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
4803 ASSERT(!HasStackOverflow()); 4746 ASSERT(!HasStackOverflow());
4804 ASSERT(current_block() != NULL); 4747 ASSERT(current_block() != NULL);
4805 ASSERT(current_block()->HasPredecessor()); 4748 ASSERT(current_block()->HasPredecessor());
4806 ASSERT(current_block() != NULL); 4749 ASSERT(current_block() != NULL);
4807 bool osr_entry = PreProcessOsrEntry(stmt); 4750 HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
4808 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4809 current_block()->Goto(loop_entry);
4810 set_current_block(loop_entry);
4811 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4812
4813 4751
4814 // If the condition is constant true, do not generate a branch. 4752 // If the condition is constant true, do not generate a branch.
4815 HBasicBlock* loop_successor = NULL; 4753 HBasicBlock* loop_successor = NULL;
4816 if (!stmt->cond()->ToBooleanIsTrue()) { 4754 if (!stmt->cond()->ToBooleanIsTrue()) {
4817 HBasicBlock* body_entry = graph()->CreateBasicBlock(); 4755 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4818 loop_successor = graph()->CreateBasicBlock(); 4756 loop_successor = graph()->CreateBasicBlock();
4819 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor)); 4757 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4820 if (body_entry->HasPredecessor()) { 4758 if (body_entry->HasPredecessor()) {
4821 body_entry->SetJoinId(stmt->BodyId()); 4759 body_entry->SetJoinId(stmt->BodyId());
4822 set_current_block(body_entry); 4760 set_current_block(body_entry);
(...skipping 21 matching lines...) Expand all
4844 4782
4845 4783
4846 void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) { 4784 void HOptimizedGraphBuilder::VisitForStatement(ForStatement* stmt) {
4847 ASSERT(!HasStackOverflow()); 4785 ASSERT(!HasStackOverflow());
4848 ASSERT(current_block() != NULL); 4786 ASSERT(current_block() != NULL);
4849 ASSERT(current_block()->HasPredecessor()); 4787 ASSERT(current_block()->HasPredecessor());
4850 if (stmt->init() != NULL) { 4788 if (stmt->init() != NULL) {
4851 CHECK_ALIVE(Visit(stmt->init())); 4789 CHECK_ALIVE(Visit(stmt->init()));
4852 } 4790 }
4853 ASSERT(current_block() != NULL); 4791 ASSERT(current_block() != NULL);
4854 bool osr_entry = PreProcessOsrEntry(stmt); 4792 HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
4855 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4856 current_block()->Goto(loop_entry);
4857 set_current_block(loop_entry);
4858 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4859 4793
4860 HBasicBlock* loop_successor = NULL; 4794 HBasicBlock* loop_successor = NULL;
4861 if (stmt->cond() != NULL) { 4795 if (stmt->cond() != NULL) {
4862 HBasicBlock* body_entry = graph()->CreateBasicBlock(); 4796 HBasicBlock* body_entry = graph()->CreateBasicBlock();
4863 loop_successor = graph()->CreateBasicBlock(); 4797 loop_successor = graph()->CreateBasicBlock();
4864 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor)); 4798 CHECK_BAILOUT(VisitForControl(stmt->cond(), body_entry, loop_successor));
4865 if (body_entry->HasPredecessor()) { 4799 if (body_entry->HasPredecessor()) {
4866 body_entry->SetJoinId(stmt->BodyId()); 4800 body_entry->SetJoinId(stmt->BodyId());
4867 set_current_block(body_entry); 4801 set_current_block(body_entry);
4868 } 4802 }
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
4932 Push(map); 4866 Push(map);
4933 Push(array); 4867 Push(array);
4934 Push(enum_length); 4868 Push(enum_length);
4935 Push(start_index); 4869 Push(start_index);
4936 4870
4937 HInstruction* index_cache = Add<HForInCacheArray>( 4871 HInstruction* index_cache = Add<HForInCacheArray>(
4938 enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex); 4872 enumerable, map, DescriptorArray::kEnumCacheBridgeIndicesCacheIndex);
4939 HForInCacheArray::cast(array)->set_index_cache( 4873 HForInCacheArray::cast(array)->set_index_cache(
4940 HForInCacheArray::cast(index_cache)); 4874 HForInCacheArray::cast(index_cache));
4941 4875
4942 bool osr_entry = PreProcessOsrEntry(stmt); 4876 HBasicBlock* loop_entry = osr_->BuildPossibleOsrLoopEntry(stmt);
4943 HBasicBlock* loop_entry = CreateLoopHeaderBlock();
4944 current_block()->Goto(loop_entry);
4945 set_current_block(loop_entry);
4946 if (osr_entry) graph()->set_osr_loop_entry(loop_entry);
4947 4877
4948 HValue* index = environment()->ExpressionStackAt(0); 4878 HValue* index = environment()->ExpressionStackAt(0);
4949 HValue* limit = environment()->ExpressionStackAt(1); 4879 HValue* limit = environment()->ExpressionStackAt(1);
4950 4880
4951 // Check that we still have more keys. 4881 // Check that we still have more keys.
4952 HCompareIDAndBranch* compare_index = 4882 HCompareIDAndBranch* compare_index =
4953 new(zone()) HCompareIDAndBranch(index, limit, Token::LT); 4883 new(zone()) HCompareIDAndBranch(index, limit, Token::LT);
4954 compare_index->set_observed_input_representation( 4884 compare_index->set_observed_input_representation(
4955 Representation::Smi(), Representation::Smi()); 4885 Representation::Smi(), Representation::Smi());
4956 4886
(...skipping 6081 matching lines...) Expand 10 before | Expand all | Expand 10 after
11038 if (ShouldProduceTraceOutput()) { 10968 if (ShouldProduceTraceOutput()) {
11039 isolate()->GetHTracer()->TraceHydrogen(name(), graph_); 10969 isolate()->GetHTracer()->TraceHydrogen(name(), graph_);
11040 } 10970 }
11041 10971
11042 #ifdef DEBUG 10972 #ifdef DEBUG
11043 graph_->Verify(false); // No full verify. 10973 graph_->Verify(false); // No full verify.
11044 #endif 10974 #endif
11045 } 10975 }
11046 10976
11047 } } // namespace v8::internal 10977 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-osr.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698