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

Side by Side Diff: src/ia32/macro-assembler-ia32.cc

Issue 6529055: [Isolates] Merge crankshaft (r5922 from bleeding_edge). (Closed)
Patch Set: Win32 port Created 9 years, 10 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
« no previous file with comments | « src/ia32/macro-assembler-ia32.h ('k') | src/ia32/stub-cache-ia32.cc » ('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 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2010 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
11 // with the distribution. 11 // with the distribution.
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 // Clobber all input registers when running with the debug-code flag 176 // Clobber all input registers when running with the debug-code flag
177 // turned on to provoke errors. 177 // turned on to provoke errors.
178 if (FLAG_debug_code) { 178 if (FLAG_debug_code) {
179 mov(object, Immediate(BitCast<int32_t>(kZapValue))); 179 mov(object, Immediate(BitCast<int32_t>(kZapValue)));
180 mov(address, Immediate(BitCast<int32_t>(kZapValue))); 180 mov(address, Immediate(BitCast<int32_t>(kZapValue)));
181 mov(value, Immediate(BitCast<int32_t>(kZapValue))); 181 mov(value, Immediate(BitCast<int32_t>(kZapValue)));
182 } 182 }
183 } 183 }
184 184
185 185
186 void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
187 cmp(esp,
188 Operand::StaticVariable(ExternalReference::address_of_stack_limit()));
189 j(below, on_stack_overflow);
190 }
191
192
193 #ifdef ENABLE_DEBUGGER_SUPPORT 186 #ifdef ENABLE_DEBUGGER_SUPPORT
194 void MacroAssembler::DebugBreak() { 187 void MacroAssembler::DebugBreak() {
195 Set(eax, Immediate(0)); 188 Set(eax, Immediate(0));
196 mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak))); 189 mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak)));
197 CEntryStub ces(1); 190 CEntryStub ces(1);
198 call(ces.GetCode(), RelocInfo::DEBUG_BREAK); 191 call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
199 } 192 }
200 #endif 193 #endif
201 194
202 195
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after
357 push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot. 350 push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
358 351
359 // Save the frame pointer and the context in top. 352 // Save the frame pointer and the context in top.
360 ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address); 353 ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address);
361 ExternalReference context_address(Isolate::k_context_address); 354 ExternalReference context_address(Isolate::k_context_address);
362 mov(Operand::StaticVariable(c_entry_fp_address), ebp); 355 mov(Operand::StaticVariable(c_entry_fp_address), ebp);
363 mov(Operand::StaticVariable(context_address), esi); 356 mov(Operand::StaticVariable(context_address), esi);
364 } 357 }
365 358
366 359
367 void MacroAssembler::EnterExitFrameEpilogue(int argc) { 360 void MacroAssembler::EnterExitFrameEpilogue(int argc, bool save_doubles) {
368 // Reserve space for arguments. 361 // Optionally save all XMM registers.
369 sub(Operand(esp), Immediate(argc * kPointerSize)); 362 if (save_doubles) {
363 CpuFeatures::Scope scope(SSE2);
364 int space = XMMRegister::kNumRegisters * kDoubleSize + argc * kPointerSize;
365 sub(Operand(esp), Immediate(space));
366 int offset = -2 * kPointerSize;
367 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
368 XMMRegister reg = XMMRegister::from_code(i);
369 movdbl(Operand(ebp, offset - ((i + 1) * kDoubleSize)), reg);
370 }
371 } else {
372 sub(Operand(esp), Immediate(argc * kPointerSize));
373 }
370 374
371 // Get the required frame alignment for the OS. 375 // Get the required frame alignment for the OS.
372 const int kFrameAlignment = OS::ActivationFrameAlignment(); 376 const int kFrameAlignment = OS::ActivationFrameAlignment();
373 if (kFrameAlignment > 0) { 377 if (kFrameAlignment > 0) {
374 ASSERT(IsPowerOf2(kFrameAlignment)); 378 ASSERT(IsPowerOf2(kFrameAlignment));
375 and_(esp, -kFrameAlignment); 379 and_(esp, -kFrameAlignment);
376 } 380 }
377 381
378 // Patch the saved entry sp. 382 // Patch the saved entry sp.
379 mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp); 383 mov(Operand(ebp, ExitFrameConstants::kSPOffset), esp);
380 } 384 }
381 385
382 386
383 void MacroAssembler::EnterExitFrame() { 387 void MacroAssembler::EnterExitFrame(bool save_doubles) {
384 EnterExitFramePrologue(); 388 EnterExitFramePrologue();
385 389
386 // Setup argc and argv in callee-saved registers. 390 // Setup argc and argv in callee-saved registers.
387 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize; 391 int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
388 mov(edi, Operand(eax)); 392 mov(edi, Operand(eax));
389 lea(esi, Operand(ebp, eax, times_4, offset)); 393 lea(esi, Operand(ebp, eax, times_4, offset));
390 394
391 // Reserve space for argc, argv and isolate. 395 // Reserve space for argc, argv and isolate.
392 EnterExitFrameEpilogue(3); 396 EnterExitFrameEpilogue(3, save_doubles);
393 } 397 }
394 398
395 399
396 void MacroAssembler::EnterApiExitFrame(int argc) { 400 void MacroAssembler::EnterApiExitFrame(int argc) {
397 EnterExitFramePrologue(); 401 EnterExitFramePrologue();
398 EnterExitFrameEpilogue(argc); 402 EnterExitFrameEpilogue(argc, false);
399 } 403 }
400 404
401 405
402 void MacroAssembler::LeaveExitFrame() { 406 void MacroAssembler::LeaveExitFrame(bool save_doubles) {
407 // Optionally restore all XMM registers.
408 if (save_doubles) {
409 CpuFeatures::Scope scope(SSE2);
410 int offset = -2 * kPointerSize;
411 for (int i = 0; i < XMMRegister::kNumRegisters; i++) {
412 XMMRegister reg = XMMRegister::from_code(i);
413 movdbl(reg, Operand(ebp, offset - ((i + 1) * kDoubleSize)));
414 }
415 }
416
403 // Get the return address from the stack and restore the frame pointer. 417 // Get the return address from the stack and restore the frame pointer.
404 mov(ecx, Operand(ebp, 1 * kPointerSize)); 418 mov(ecx, Operand(ebp, 1 * kPointerSize));
405 mov(ebp, Operand(ebp, 0 * kPointerSize)); 419 mov(ebp, Operand(ebp, 0 * kPointerSize));
406 420
407 // Pop the arguments and the receiver from the caller stack. 421 // Pop the arguments and the receiver from the caller stack.
408 lea(esp, Operand(esi, 1 * kPointerSize)); 422 lea(esp, Operand(esi, 1 * kPointerSize));
409 423
410 // Push the return address to get ready to return. 424 // Push the return address to get ready to return.
411 push(ecx); 425 push(ecx);
412 426
(...skipping 680 matching lines...) Expand 10 before | Expand all | Expand 10 after
1093 mov(index, hash); 1107 mov(index, hash);
1094 } 1108 }
1095 } 1109 }
1096 1110
1097 1111
1098 void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) { 1112 void MacroAssembler::CallRuntime(Runtime::FunctionId id, int num_arguments) {
1099 CallRuntime(Runtime::FunctionForId(id), num_arguments); 1113 CallRuntime(Runtime::FunctionForId(id), num_arguments);
1100 } 1114 }
1101 1115
1102 1116
1117 void MacroAssembler::CallRuntimeSaveDoubles(Runtime::FunctionId id) {
1118 const Runtime::Function* function = Runtime::FunctionForId(id);
1119 Set(eax, Immediate(function->nargs));
1120 mov(ebx, Immediate(ExternalReference(function)));
1121 CEntryStub ces(1);
1122 ces.SaveDoubles();
1123 CallStub(&ces);
1124 }
1125
1126
1103 MaybeObject* MacroAssembler::TryCallRuntime(Runtime::FunctionId id, 1127 MaybeObject* MacroAssembler::TryCallRuntime(Runtime::FunctionId id,
1104 int num_arguments) { 1128 int num_arguments) {
1105 return TryCallRuntime(Runtime::FunctionForId(id), num_arguments); 1129 return TryCallRuntime(Runtime::FunctionForId(id), num_arguments);
1106 } 1130 }
1107 1131
1108 1132
1109 void MacroAssembler::CallRuntime(const Runtime::Function* f, 1133 void MacroAssembler::CallRuntime(const Runtime::Function* f,
1110 int num_arguments) { 1134 int num_arguments) {
1111 // If the expected number of arguments of the runtime function is 1135 // If the expected number of arguments of the runtime function is
1112 // constant, we check that the actual number of arguments match the 1136 // constant, we check that the actual number of arguments match the
(...skipping 220 matching lines...) Expand 10 before | Expand all | Expand 10 after
1333 CEntryStub ces(1); 1357 CEntryStub ces(1);
1334 return TryTailCallStub(&ces); 1358 return TryTailCallStub(&ces);
1335 } 1359 }
1336 1360
1337 1361
1338 void MacroAssembler::InvokePrologue(const ParameterCount& expected, 1362 void MacroAssembler::InvokePrologue(const ParameterCount& expected,
1339 const ParameterCount& actual, 1363 const ParameterCount& actual,
1340 Handle<Code> code_constant, 1364 Handle<Code> code_constant,
1341 const Operand& code_operand, 1365 const Operand& code_operand,
1342 Label* done, 1366 Label* done,
1343 InvokeFlag flag) { 1367 InvokeFlag flag,
1368 PostCallGenerator* post_call_generator) {
1344 bool definitely_matches = false; 1369 bool definitely_matches = false;
1345 Label invoke; 1370 Label invoke;
1346 if (expected.is_immediate()) { 1371 if (expected.is_immediate()) {
1347 ASSERT(actual.is_immediate()); 1372 ASSERT(actual.is_immediate());
1348 if (expected.immediate() == actual.immediate()) { 1373 if (expected.immediate() == actual.immediate()) {
1349 definitely_matches = true; 1374 definitely_matches = true;
1350 } else { 1375 } else {
1351 mov(eax, actual.immediate()); 1376 mov(eax, actual.immediate());
1352 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel; 1377 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel;
1353 if (expected.immediate() == sentinel) { 1378 if (expected.immediate() == sentinel) {
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
1385 Builtins::ArgumentsAdaptorTrampoline)); 1410 Builtins::ArgumentsAdaptorTrampoline));
1386 if (!code_constant.is_null()) { 1411 if (!code_constant.is_null()) {
1387 mov(edx, Immediate(code_constant)); 1412 mov(edx, Immediate(code_constant));
1388 add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag)); 1413 add(Operand(edx), Immediate(Code::kHeaderSize - kHeapObjectTag));
1389 } else if (!code_operand.is_reg(edx)) { 1414 } else if (!code_operand.is_reg(edx)) {
1390 mov(edx, code_operand); 1415 mov(edx, code_operand);
1391 } 1416 }
1392 1417
1393 if (flag == CALL_FUNCTION) { 1418 if (flag == CALL_FUNCTION) {
1394 call(adaptor, RelocInfo::CODE_TARGET); 1419 call(adaptor, RelocInfo::CODE_TARGET);
1420 if (post_call_generator != NULL) post_call_generator->Generate();
1395 jmp(done); 1421 jmp(done);
1396 } else { 1422 } else {
1397 jmp(adaptor, RelocInfo::CODE_TARGET); 1423 jmp(adaptor, RelocInfo::CODE_TARGET);
1398 } 1424 }
1399 bind(&invoke); 1425 bind(&invoke);
1400 } 1426 }
1401 } 1427 }
1402 1428
1403 1429
1404 void MacroAssembler::InvokeCode(const Operand& code, 1430 void MacroAssembler::InvokeCode(const Operand& code,
1405 const ParameterCount& expected, 1431 const ParameterCount& expected,
1406 const ParameterCount& actual, 1432 const ParameterCount& actual,
1407 InvokeFlag flag) { 1433 InvokeFlag flag,
1434 PostCallGenerator* post_call_generator) {
1408 Label done; 1435 Label done;
1409 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag); 1436 InvokePrologue(expected, actual, Handle<Code>::null(), code,
1437 &done, flag, post_call_generator);
1410 if (flag == CALL_FUNCTION) { 1438 if (flag == CALL_FUNCTION) {
1411 call(code); 1439 call(code);
1440 if (post_call_generator != NULL) post_call_generator->Generate();
1412 } else { 1441 } else {
1413 ASSERT(flag == JUMP_FUNCTION); 1442 ASSERT(flag == JUMP_FUNCTION);
1414 jmp(code); 1443 jmp(code);
1415 } 1444 }
1416 bind(&done); 1445 bind(&done);
1417 } 1446 }
1418 1447
1419 1448
1420 void MacroAssembler::InvokeCode(Handle<Code> code, 1449 void MacroAssembler::InvokeCode(Handle<Code> code,
1421 const ParameterCount& expected, 1450 const ParameterCount& expected,
1422 const ParameterCount& actual, 1451 const ParameterCount& actual,
1423 RelocInfo::Mode rmode, 1452 RelocInfo::Mode rmode,
1424 InvokeFlag flag) { 1453 InvokeFlag flag,
1454 PostCallGenerator* post_call_generator) {
1425 Label done; 1455 Label done;
1426 Operand dummy(eax); 1456 Operand dummy(eax);
1427 InvokePrologue(expected, actual, code, dummy, &done, flag); 1457 InvokePrologue(expected, actual, code, dummy, &done,
1458 flag, post_call_generator);
1428 if (flag == CALL_FUNCTION) { 1459 if (flag == CALL_FUNCTION) {
1429 call(code, rmode); 1460 call(code, rmode);
1461 if (post_call_generator != NULL) post_call_generator->Generate();
1430 } else { 1462 } else {
1431 ASSERT(flag == JUMP_FUNCTION); 1463 ASSERT(flag == JUMP_FUNCTION);
1432 jmp(code, rmode); 1464 jmp(code, rmode);
1433 } 1465 }
1434 bind(&done); 1466 bind(&done);
1435 } 1467 }
1436 1468
1437 1469
1438 void MacroAssembler::InvokeFunction(Register fun, 1470 void MacroAssembler::InvokeFunction(Register fun,
1439 const ParameterCount& actual, 1471 const ParameterCount& actual,
1440 InvokeFlag flag) { 1472 InvokeFlag flag,
1473 PostCallGenerator* post_call_generator) {
1441 ASSERT(fun.is(edi)); 1474 ASSERT(fun.is(edi));
1442 mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset)); 1475 mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
1443 mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 1476 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
1444 mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset)); 1477 mov(ebx, FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
1445 SmiUntag(ebx); 1478 SmiUntag(ebx);
1446 1479
1447 ParameterCount expected(ebx); 1480 ParameterCount expected(ebx);
1448 InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), 1481 InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
1449 expected, actual, flag); 1482 expected, actual, flag, post_call_generator);
1450 } 1483 }
1451 1484
1452 1485
1453 void MacroAssembler::InvokeFunction(JSFunction* function, 1486 void MacroAssembler::InvokeFunction(JSFunction* function,
1454 const ParameterCount& actual, 1487 const ParameterCount& actual,
1455 InvokeFlag flag) { 1488 InvokeFlag flag,
1489 PostCallGenerator* post_call_generator) {
1456 ASSERT(function->is_compiled()); 1490 ASSERT(function->is_compiled());
1457 // Get the function and setup the context. 1491 // Get the function and setup the context.
1458 mov(edi, Immediate(Handle<JSFunction>(function))); 1492 mov(edi, Immediate(Handle<JSFunction>(function)));
1459 mov(esi, FieldOperand(edi, JSFunction::kContextOffset)); 1493 mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
1460 // Invoke the cached code. 1494
1461 Handle<Code> code(function->code());
1462 ParameterCount expected(function->shared()->formal_parameter_count()); 1495 ParameterCount expected(function->shared()->formal_parameter_count());
1463 InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag); 1496 if (V8::UseCrankshaft()) {
1497 // TODO(kasperl): For now, we always call indirectly through the
1498 // code field in the function to allow recompilation to take effect
1499 // without changing any of the call sites.
1500 InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
1501 expected, actual, flag, post_call_generator);
1502 } else {
1503 Handle<Code> code(function->code());
1504 InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET,
1505 flag, post_call_generator);
1506 }
1464 } 1507 }
1465 1508
1466 1509
1467 void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) { 1510 void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
1511 InvokeFlag flag,
1512 PostCallGenerator* post_call_generator) {
1468 // Calls are not allowed in some stubs. 1513 // Calls are not allowed in some stubs.
1469 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls()); 1514 ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
1470 1515
1471 // Rely on the assertion to check that the number of provided 1516 // Rely on the assertion to check that the number of provided
1472 // arguments match the expected number of arguments. Fake a 1517 // arguments match the expected number of arguments. Fake a
1473 // parameter count to avoid emitting code to do the check. 1518 // parameter count to avoid emitting code to do the check.
1474 ParameterCount expected(0); 1519 ParameterCount expected(0);
1475 GetBuiltinFunction(edi, id); 1520 GetBuiltinFunction(edi, id);
1476 InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), 1521 InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset),
1477 expected, expected, flag); 1522 expected, expected, flag, post_call_generator);
1478 } 1523 }
1479 1524
1480 void MacroAssembler::GetBuiltinFunction(Register target, 1525 void MacroAssembler::GetBuiltinFunction(Register target,
1481 Builtins::JavaScript id) { 1526 Builtins::JavaScript id) {
1482 // Load the JavaScript builtin function from the builtins object. 1527 // Load the JavaScript builtin function from the builtins object.
1483 mov(target, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX))); 1528 mov(target, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
1484 mov(target, FieldOperand(target, GlobalObject::kBuiltinsOffset)); 1529 mov(target, FieldOperand(target, GlobalObject::kBuiltinsOffset));
1485 mov(target, FieldOperand(target, 1530 mov(target, FieldOperand(target,
1486 JSBuiltinsObject::OffsetOfFunctionWithId(id))); 1531 JSBuiltinsObject::OffsetOfFunctionWithId(id)));
1487 } 1532 }
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
1532 Label ok, fail; 1577 Label ok, fail;
1533 CheckMap(map, FACTORY->meta_map(), &fail, false); 1578 CheckMap(map, FACTORY->meta_map(), &fail, false);
1534 jmp(&ok); 1579 jmp(&ok);
1535 bind(&fail); 1580 bind(&fail);
1536 Abort("Global functions must have initial map"); 1581 Abort("Global functions must have initial map");
1537 bind(&ok); 1582 bind(&ok);
1538 } 1583 }
1539 } 1584 }
1540 1585
1541 1586
1587 int MacroAssembler::SafepointRegisterStackIndex(int reg_code) {
1588 // The registers are pushed starting with the lowest encoding,
1589 // which means that lowest encodings are furthest away from
1590 // the stack pointer.
1591 ASSERT(reg_code >= 0 && reg_code < kNumSafepointRegisters);
1592 return kNumSafepointRegisters - reg_code - 1;
1593 }
1594
1595
1542 void MacroAssembler::Ret() { 1596 void MacroAssembler::Ret() {
1543 ret(0); 1597 ret(0);
1544 } 1598 }
1545 1599
1546 1600
1547 void MacroAssembler::Drop(int stack_elements) { 1601 void MacroAssembler::Drop(int stack_elements) {
1548 if (stack_elements > 0) { 1602 if (stack_elements > 0) {
1549 add(Operand(esp), Immediate(stack_elements * kPointerSize)); 1603 add(Operand(esp), Immediate(stack_elements * kPointerSize));
1550 } 1604 }
1551 } 1605 }
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
1870 1924
1871 // Check that the code was patched as expected. 1925 // Check that the code was patched as expected.
1872 ASSERT(masm_.pc_ == address_ + size_); 1926 ASSERT(masm_.pc_ == address_ + size_);
1873 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap); 1927 ASSERT(masm_.reloc_info_writer.pos() == address_ + size_ + Assembler::kGap);
1874 } 1928 }
1875 1929
1876 1930
1877 } } // namespace v8::internal 1931 } } // namespace v8::internal
1878 1932
1879 #endif // V8_TARGET_ARCH_IA32 1933 #endif // V8_TARGET_ARCH_IA32
OLDNEW
« no previous file with comments | « src/ia32/macro-assembler-ia32.h ('k') | src/ia32/stub-cache-ia32.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698