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

Side by Side Diff: src/x64/fast-codegen-x64.cc

Issue 339004: Eliminate the constant location used for literals in the AST.... (Closed) Base URL: http://v8.googlecode.com/svn/branches/bleeding_edge/
Patch Set: Created 11 years, 1 month 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
« src/ia32/fast-codegen-ia32.cc ('K') | « src/location.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2009 the V8 project authors. All rights reserved. 1 // Copyright 2009 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 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
137 Comment cmnt(masm_, "[ ExpressionStatement"); 137 Comment cmnt(masm_, "[ ExpressionStatement");
138 SetStatementPosition(stmt); 138 SetStatementPosition(stmt);
139 Visit(stmt->expression()); 139 Visit(stmt->expression());
140 } 140 }
141 141
142 142
143 void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) { 143 void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
144 Comment cmnt(masm_, "[ ReturnStatement"); 144 Comment cmnt(masm_, "[ ReturnStatement");
145 SetStatementPosition(stmt); 145 SetStatementPosition(stmt);
146 Expression* expr = stmt->expression(); 146 Expression* expr = stmt->expression();
147 Visit(expr); 147 // Complete the statement based on the type of the subexpression.
148 if (expr->AsLiteral() != NULL) {
149 __ Move(rax, expr->AsLiteral()->handle());
150 } else {
151 Visit(expr);
152 ASSERT(expr->location().is_temporary());
153 __ pop(rax);
154 }
148 155
149 // Complete the statement based on the location of the subexpression.
150 Location source = expr->location();
151 ASSERT(!source.is_nowhere());
152 if (source.is_temporary()) {
153 __ pop(rax);
154 } else {
155 ASSERT(source.is_constant());
156 ASSERT(expr->AsLiteral() != NULL);
157 __ Move(rax, expr->AsLiteral()->handle());
158 }
159 if (FLAG_trace) { 156 if (FLAG_trace) {
160 __ push(rax); 157 __ push(rax);
161 __ CallRuntime(Runtime::kTraceExit, 1); 158 __ CallRuntime(Runtime::kTraceExit, 1);
162 } 159 }
163 160
164 __ RecordJSReturn(); 161 __ RecordJSReturn();
165 // Do not use the leave instruction here because it is too short to 162 // Do not use the leave instruction here because it is too short to
166 // patch with the code required by the debugger. 163 // patch with the code required by the debugger.
167 __ movq(rsp, rbp); 164 __ movq(rsp, rbp);
168 __ pop(rbp); 165 __ pop(rbp);
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
230 ASSERT(slot != NULL); 227 ASSERT(slot != NULL);
231 if (expr->location().is_temporary()) { 228 if (expr->location().is_temporary()) {
232 __ push(Operand(rbp, SlotOffset(slot))); 229 __ push(Operand(rbp, SlotOffset(slot)));
233 } else { 230 } else {
234 ASSERT(expr->location().is_nowhere()); 231 ASSERT(expr->location().is_nowhere());
235 } 232 }
236 } 233 }
237 } 234 }
238 235
239 236
237 void FastCodeGenerator::VisitLiteral(Literal* expr) {
238 if (expr->location().is_temporary()) {
239 __ Push(expr->handle());
240 } else {
241 ASSERT(expr->location().is_nowhere());
242 }
243 }
244
245
240 void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { 246 void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
241 Comment cmnt(masm_, "[ ObjectLiteral"); 247 Comment cmnt(masm_, "[ ObjectLiteral");
242 Label boilerplate_exists; 248 Label boilerplate_exists;
243 249
244 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset)); 250 __ movq(rdi, Operand(rbp, JavaScriptFrameConstants::kFunctionOffset));
245 __ movq(rbx, FieldOperand(rdi, JSFunction::kLiteralsOffset)); 251 __ movq(rbx, FieldOperand(rdi, JSFunction::kLiteralsOffset));
246 int literal_offset = 252 int literal_offset =
247 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; 253 FixedArray::kHeaderSize + expr->literal_index() * kPointerSize;
248 __ movq(rax, FieldOperand(rbx, literal_offset)); 254 __ movq(rax, FieldOperand(rbx, literal_offset));
249 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); 255 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
294 __ Move(rcx, key->handle()); 300 __ Move(rcx, key->handle());
295 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); 301 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
296 __ call(ic, RelocInfo::CODE_TARGET); 302 __ call(ic, RelocInfo::CODE_TARGET);
297 // StoreIC leaves the receiver on the stack. 303 // StoreIC leaves the receiver on the stack.
298 break; 304 break;
299 } 305 }
300 // fall through 306 // fall through
301 case ObjectLiteral::Property::PROTOTYPE: 307 case ObjectLiteral::Property::PROTOTYPE:
302 __ push(rax); 308 __ push(rax);
303 Visit(key); 309 Visit(key);
304 if (key->location().is_constant()) { 310 ASSERT(key->location().is_temporary());
305 __ Push(key->handle());
306 }
307 Visit(value); 311 Visit(value);
308 ASSERT(value->location().is_temporary()); 312 ASSERT(value->location().is_temporary());
309 __ CallRuntime(Runtime::kSetProperty, 3); 313 __ CallRuntime(Runtime::kSetProperty, 3);
310 __ movq(rax, Operand(rsp, 0)); // Restore result into rax. 314 __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
311 break; 315 break;
312 case ObjectLiteral::Property::SETTER: // fall through 316 case ObjectLiteral::Property::SETTER: // fall through
313 case ObjectLiteral::Property::GETTER: 317 case ObjectLiteral::Property::GETTER:
314 __ push(rax); 318 __ push(rax);
315 Visit(key); 319 Visit(key);
316 if (key->location().is_constant()) { 320 ASSERT(key->location.is_temporary());
317 __ Push(key->handle());
318 }
319 __ Push(property->kind() == ObjectLiteral::Property::SETTER ? 321 __ Push(property->kind() == ObjectLiteral::Property::SETTER ?
320 Smi::FromInt(1) : 322 Smi::FromInt(1) :
321 Smi::FromInt(0)); 323 Smi::FromInt(0));
322 Visit(value); 324 Visit(value);
323 ASSERT(value->location().is_temporary()); 325 ASSERT(value->location().is_temporary());
324 __ CallRuntime(Runtime::kDefineAccessor, 4); 326 __ CallRuntime(Runtime::kDefineAccessor, 4);
325 __ movq(rax, Operand(rsp, 0)); // Restore result into rax. 327 __ movq(rax, Operand(rsp, 0)); // Restore result into rax.
326 break; 328 break;
327 default: UNREACHABLE(); 329 default: UNREACHABLE();
328 } 330 }
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
432 __ addq(rsp, Immediate(kPointerSize)); 434 __ addq(rsp, Immediate(kPointerSize));
433 } else if (destination.is_temporary() && !result_saved) { 435 } else if (destination.is_temporary() && !result_saved) {
434 __ push(rax); 436 __ push(rax);
435 } 437 }
436 } 438 }
437 439
438 440
439 void FastCodeGenerator::VisitAssignment(Assignment* expr) { 441 void FastCodeGenerator::VisitAssignment(Assignment* expr) {
440 Comment cmnt(masm_, "[ Assignment"); 442 Comment cmnt(masm_, "[ Assignment");
441 ASSERT(expr->op() == Token::ASSIGN || expr->op() == Token::INIT_VAR); 443 ASSERT(expr->op() == Token::ASSIGN || expr->op() == Token::INIT_VAR);
442 Expression* rhs = expr->value();
443 Visit(rhs);
444 444
445 // Left-hand side can only be a global or a (parameter or local) slot. 445 // Left-hand side can only be a global or a (parameter or local) slot.
446 Variable* var = expr->target()->AsVariableProxy()->AsVariable(); 446 Variable* var = expr->target()->AsVariableProxy()->AsVariable();
447 ASSERT(var != NULL); 447 ASSERT(var != NULL);
448 ASSERT(var->is_global() || var->slot() != NULL); 448 ASSERT(var->is_global() || var->slot() != NULL);
449 449
450 // Complete the assignment based on the location of the right-hand-side 450 Expression* rhs = expr->value();
451 // value and the desired location of the assignment value.
452 Location destination = expr->location(); 451 Location destination = expr->location();
453 Location source = rhs->location();
454 ASSERT(!destination.is_constant());
455 ASSERT(!source.is_nowhere());
456
457 if (var->is_global()) { 452 if (var->is_global()) {
458 // Assignment to a global variable, use inline caching. Right-hand-side 453 // Assignment to a global variable, use inline caching. Right-hand-side
459 // value is passed in rax, variable name in rcx, and the global object 454 // value is passed in rax, variable name in rcx, and the global object
460 // on the stack. 455 // on the stack.
461 if (source.is_temporary()) { 456
457 // Code for the right-hand-side expression depends on its type.
458 if (rhs->AsLiteral() != NULL) {
459 __ Move(rax, rhs->AsLiteral()->handle());
460 } else {
461 ASSERT(rhs->location().is_temporary());
462 Visit(rhs);
462 __ pop(rax); 463 __ pop(rax);
463 } else {
464 ASSERT(source.is_constant());
465 ASSERT(rhs->AsLiteral() != NULL);
466 __ Move(rax, rhs->AsLiteral()->handle());
467 } 464 }
468 __ Move(rcx, var->name()); 465 __ Move(rcx, var->name());
469 __ push(CodeGenerator::GlobalObject()); 466 __ push(CodeGenerator::GlobalObject());
470 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize)); 467 Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
471 __ Call(ic, RelocInfo::CODE_TARGET); 468 __ Call(ic, RelocInfo::CODE_TARGET);
472 // Overwrite the global object on the stack with the result if needed. 469 // Overwrite the global object on the stack with the result if needed.
473 if (destination.is_temporary()) { 470 if (destination.is_temporary()) {
474 __ movq(Operand(rsp, 0), rax); 471 __ movq(Operand(rsp, 0), rax);
475 } else { 472 } else {
476 __ addq(rsp, Immediate(kPointerSize)); 473 __ addq(rsp, Immediate(kPointerSize));
477 } 474 }
478 } else { 475 } else {
479 if (source.is_temporary()) { 476 // Local or parameter assignment.
477
478 // Code for the right-hand-side expression depends on its type.
479 if (rhs->AsLiteral() != NULL) {
480 // Two cases: 'temp <- (var = constant)', or 'var = constant' with a
481 // discarded result. Always perform the assignment.
482 __ Move(kScratchRegister, rhs->AsLiteral()->handle());
483 __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister);
484 if (destination.is_temporary()) {
485 // Case 'temp <- (var = constant)'. Save result.
486 __ push(kScratchRegister);
487 }
fschneider 2009/10/26 17:57:20 Add an assert here to be consistent with the rest
488 } else {
489 ASSERT(rhs->location().is_temporary());
490 Visit(rhs);
480 if (destination.is_temporary()) { 491 if (destination.is_temporary()) {
481 // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary 492 // Case 'temp1 <- (var = temp0)'. Preserve right-hand-side temporary
482 // on the stack. 493 // on the stack.
483 __ movq(kScratchRegister, Operand(rsp, 0)); 494 __ movq(kScratchRegister, Operand(rsp, 0));
484 __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister); 495 __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister);
485 } else { 496 } else {
486 ASSERT(destination.is_nowhere()); 497 ASSERT(destination.is_nowhere());
487 // Case 'var = temp'. Discard right-hand-side temporary. 498 // Case 'var = temp'. Discard right-hand-side temporary.
488 __ pop(Operand(rbp, SlotOffset(var->slot()))); 499 __ pop(Operand(rbp, SlotOffset(var->slot())));
489 } 500 }
490 } else {
491 ASSERT(source.is_constant());
492 ASSERT(rhs->AsLiteral() != NULL);
493 // Two cases: 'temp <- (var = constant)', or 'var = constant' with a
494 // discarded result. Always perform the assignment.
495 __ Move(kScratchRegister, rhs->AsLiteral()->handle());
496 __ movq(Operand(rbp, SlotOffset(var->slot())), kScratchRegister);
497 if (destination.is_temporary()) {
498 // Case 'temp <- (var = constant)'. Save result.
499 __ push(kScratchRegister);
500 }
501 } 501 }
502 } 502 }
503 } 503 }
504 504
505 505
506 void FastCodeGenerator::VisitCall(Call* expr) { 506 void FastCodeGenerator::VisitCall(Call* expr) {
507 Expression* fun = expr->expression(); 507 Expression* fun = expr->expression();
508 ZoneList<Expression*>* args = expr->arguments(); 508 ZoneList<Expression*>* args = expr->arguments();
509 Variable* var = fun->AsVariableProxy()->AsVariable(); 509 Variable* var = fun->AsVariableProxy()->AsVariable();
510 ASSERT(var != NULL && !var->is_this() && var->is_global()); 510 ASSERT(var != NULL && !var->is_this() && var->is_global());
511 ASSERT(!var->is_possibly_eval()); 511 ASSERT(!var->is_possibly_eval());
512 512
513 __ Push(var->name()); 513 __ Push(var->name());
514 // Push global object (receiver). 514 // Push global object (receiver).
515 __ push(CodeGenerator::GlobalObject()); 515 __ push(CodeGenerator::GlobalObject());
516 int arg_count = args->length(); 516 int arg_count = args->length();
517 for (int i = 0; i < arg_count; i++) { 517 for (int i = 0; i < arg_count; i++) {
518 Visit(args->at(i)); 518 Visit(args->at(i));
519 ASSERT(!args->at(i)->location().is_nowhere()); 519 ASSERT(args->at(i)->location().is_temporary());
520 if (args->at(i)->location().is_constant()) {
521 ASSERT(args->at(i)->AsLiteral() != NULL);
522 __ Push(args->at(i)->AsLiteral()->handle());
523 }
524 } 520 }
525 // Record source position for debugger 521 // Record source position for debugger
526 SetSourcePosition(expr->position()); 522 SetSourcePosition(expr->position());
527 // Call the IC initialization code. 523 // Call the IC initialization code.
528 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, 524 Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
529 NOT_IN_LOOP); 525 NOT_IN_LOOP);
530 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); 526 __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
531 // Restore context register. 527 // Restore context register.
532 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); 528 __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
533 // Discard the function left on TOS. 529 // Discard the function left on TOS.
(...skipping 10 matching lines...) Expand all
544 Comment cmnt(masm_, "[ CallRuntime"); 540 Comment cmnt(masm_, "[ CallRuntime");
545 ZoneList<Expression*>* args = expr->arguments(); 541 ZoneList<Expression*>* args = expr->arguments();
546 Runtime::Function* function = expr->function(); 542 Runtime::Function* function = expr->function();
547 543
548 ASSERT(function != NULL); 544 ASSERT(function != NULL);
549 545
550 // Push the arguments ("left-to-right"). 546 // Push the arguments ("left-to-right").
551 int arg_count = args->length(); 547 int arg_count = args->length();
552 for (int i = 0; i < arg_count; i++) { 548 for (int i = 0; i < arg_count; i++) {
553 Visit(args->at(i)); 549 Visit(args->at(i));
554 ASSERT(!args->at(i)->location().is_nowhere()); 550 ASSERT(args->at(i)->location().is_temporary());
555 if (args->at(i)->location().is_constant()) {
556 ASSERT(args->at(i)->AsLiteral() != NULL);
557 __ Push(args->at(i)->AsLiteral()->handle());
558 } else {
559 ASSERT(args->at(i)->location().is_temporary());
560 // If location is temporary, it is already on the stack,
561 // so nothing to do here.
562 }
563 } 551 }
564 552
565 __ CallRuntime(function, arg_count); 553 __ CallRuntime(function, arg_count);
566 if (expr->location().is_temporary()) { 554 if (expr->location().is_temporary()) {
567 __ push(rax); 555 __ push(rax);
568 } else { 556 } else {
569 ASSERT(expr->location().is_nowhere()); 557 ASSERT(expr->location().is_nowhere());
570 } 558 }
571 } 559 }
572 560
573 561
574 void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) { 562 void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
575 // Compile a short-circuited boolean or operation in a non-test 563 // Compile a short-circuited boolean or operation in a non-test
576 // context. 564 // context.
577 ASSERT(expr->op() == Token::OR); 565 ASSERT(expr->op() == Token::OR);
578 // Compile (e0 || e1) as if it were 566 // Compile (e0 || e1) as if it were
579 // (let (temp = e0) temp ? temp : e1). 567 // (let (temp = e0) temp ? temp : e1).
580 568
581 Label eval_right, done; 569 Label eval_right, done;
582 Location destination = expr->location(); 570 Location destination = expr->location();
583 ASSERT(!destination.is_constant()); 571 Expression* left = expr->left();
572 Expression* right = expr->right();
584 573
585 Expression* left = expr->left();
586 Location left_source = left->location();
587 ASSERT(!left_source.is_nowhere());
588
589 Expression* right = expr->right();
590 Location right_source = right->location();
591 ASSERT(!right_source.is_nowhere());
592
593 Visit(left);
594 // Use the shared ToBoolean stub to find the boolean value of the 574 // Use the shared ToBoolean stub to find the boolean value of the
595 // left-hand subexpression. Load the value into rax to perform some 575 // left-hand subexpression. Load the value into rax to perform some
596 // inlined checks assumed by the stub. 576 // inlined checks assumed by the stub.
597 if (left_source.is_temporary()) { 577
578 // Compile the left-hand value into rax. Put it on the stack if we may
579 // need it as the value of the whole expression.
580 if (left->AsLiteral() != NULL) {
581 __ Move(rax, left->AsLiteral()->handle());
582 if (destination.is_temporary()) __ push(rax);
fschneider 2009/10/26 17:57:20 May want to add an assert here to be consistent wi
583 } else {
584 Visit(left);
585 ASSERT(left->location().is_temporary());
598 if (destination.is_temporary()) { 586 if (destination.is_temporary()) {
599 // Copy the left-hand value into rax because we may need it as the 587 // Copy the left-hand value into rax because we may need it as the
600 // final result. 588 // final result.
601 __ movq(rax, Operand(rsp, 0)); 589 __ movq(rax, Operand(rsp, 0));
602 } else { 590 } else {
603 // Pop the left-hand value into rax because we will not need it as the 591 // Pop the left-hand value into rax because we will not need it as the
604 // final result. 592 // final result.
605 __ pop(rax); 593 __ pop(rax);
fschneider 2009/10/26 17:57:20 Adding an assert to be consistent on all platforms
606 } 594 }
607 } else {
608 // Load the left-hand value into rax. Put it on the stack if we may
609 // need it.
610 ASSERT(left->AsLiteral() != NULL);
611 __ Move(rax, left->AsLiteral()->handle());
612 if (destination.is_temporary()) __ push(rax);
613 } 595 }
614 // The left-hand value is in rax. It is also on the stack iff the 596 // The left-hand value is in rax. It is also on the stack iff the
615 // destination location is temporary. 597 // destination location is temporary.
616 598
617 // Perform fast checks assumed by the stub. 599 // Perform fast checks assumed by the stub.
618 // The undefined value is false. 600 // The undefined value is false.
619 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex); 601 __ CompareRoot(rax, Heap::kUndefinedValueRootIndex);
620 __ j(equal, &eval_right); 602 __ j(equal, &eval_right);
621 __ CompareRoot(rax, Heap::kTrueValueRootIndex); // True is true. 603 __ CompareRoot(rax, Heap::kTrueValueRootIndex); // True is true.
622 __ j(equal, &done); 604 __ j(equal, &done);
(...skipping 10 matching lines...) Expand all
633 ToBooleanStub stub; 615 ToBooleanStub stub;
634 __ CallStub(&stub); 616 __ CallStub(&stub);
635 __ testq(rax, rax); // The stub returns nonzero for true. 617 __ testq(rax, rax); // The stub returns nonzero for true.
636 __ j(not_zero, &done); 618 __ j(not_zero, &done);
637 619
638 __ bind(&eval_right); 620 __ bind(&eval_right);
639 // Discard the left-hand value if present on the stack. 621 // Discard the left-hand value if present on the stack.
640 if (destination.is_temporary()) { 622 if (destination.is_temporary()) {
641 __ addq(rsp, Immediate(kPointerSize)); 623 __ addq(rsp, Immediate(kPointerSize));
642 } 624 }
643 Visit(right);
644
645 // Save or discard the right-hand value as needed. 625 // Save or discard the right-hand value as needed.
646 if (destination.is_temporary() && right_source.is_constant()) { 626 if (right->AsLiteral() != NULL) {
647 ASSERT(right->AsLiteral() != NULL); 627 if (destination.is_temporary()) {
648 __ Push(right->AsLiteral()->handle()); 628 __ Push(right->AsLiteral()->handle());
649 } else if (destination.is_nowhere() && right_source.is_temporary()) { 629 } else {
650 __ addq(rsp, Immediate(kPointerSize)); 630 ASSERT(destination.is_nowhere());
631 }
632 } else {
633 Visit(right);
634 ASSERT(right->location().is_temporary());
635 if (destination.is_nowhere()) {
636 __ addq(rsp, Immediate(kPointerSize));
637 } else {
638 ASSERT(destination.is_temporary());
639 }
651 } 640 }
652 641
653 __ bind(&done); 642 __ bind(&done);
654 } 643 }
655 644
656 645
657 } } // namespace v8::internal 646 } } // namespace v8::internal
OLDNEW
« src/ia32/fast-codegen-ia32.cc ('K') | « src/location.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698