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

Unified Diff: runtime/vm/opt_code_generator_ia32.cc

Issue 8588002: Better tracing of deoptimization (added a reason id and printing of source). Fix a bug with exces... (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 9 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/vm/opt_code_generator_ia32.h ('k') | runtime/vm/stub_code_ia32.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/vm/opt_code_generator_ia32.cc
===================================================================
--- runtime/vm/opt_code_generator_ia32.cc (revision 1585)
+++ runtime/vm/opt_code_generator_ia32.cc (working copy)
@@ -115,7 +115,11 @@
// be pushed for the deoptimization point in unoptimized code.
class DeoptimizationBlob : public ZoneAllocated {
public:
- explicit DeoptimizationBlob(AstNode* node) : node_(node), registers_(2) {}
+ DeoptimizationBlob(AstNode* node, DeoptReasonId deopt_reason_id)
+ : node_(node),
+ registers_(2),
+ label_(),
+ deopt_reason_id_(deopt_reason_id) {}
void Push(Register reg) { registers_.Add(reg); }
@@ -124,6 +128,7 @@
for (int i = 0; i < registers_.length(); i++) {
codegen->assembler()->pushl(registers_[i]);
}
+ codegen->assembler()->movl(EAX, Immediate(Smi::RawValue(deopt_reason_id_)));
codegen->CallDeoptimize(node_->id(), node_->token_index());
#if defined(DEBUG)
// Check that deoptimization point exists in unoptimized code.
@@ -143,6 +148,7 @@
const AstNode* node_;
GrowableArray<Register> registers_;
Label label_;
+ DeoptReasonId deopt_reason_id_;
DISALLOW_COPY_AND_ASSIGN(DeoptimizationBlob);
};
@@ -219,16 +225,19 @@
DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node) {
- DeoptimizationBlob* d = new DeoptimizationBlob(node);
+OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
+ DeoptReasonId reason_id) {
+ DeoptimizationBlob* d = new DeoptimizationBlob(node, reason_id);
deoptimization_blobs_.Add(d);
return d;
}
DeoptimizationBlob*
-OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node, Register reg) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node);
+OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
+ Register reg,
+ DeoptReasonId reason_id) {
+ DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
d->Push(reg);
return d;
}
@@ -237,8 +246,9 @@
DeoptimizationBlob*
OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
Register reg1,
- Register reg2) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node);
+ Register reg2,
+ DeoptReasonId reason_id) {
+ DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
d->Push(reg1);
d->Push(reg2);
return d;
@@ -249,8 +259,9 @@
OptimizingCodeGenerator::AddDeoptimizationBlob(AstNode* node,
Register reg1,
Register reg2,
- Register reg3) {
- DeoptimizationBlob* d = AddDeoptimizationBlob(node);
+ Register reg3,
+ DeoptReasonId reason_id) {
+ DeoptimizationBlob* d = AddDeoptimizationBlob(node, reason_id);
d->Push(reg1);
d->Push(reg2);
d->Push(reg3);
@@ -672,7 +683,8 @@
Label* overflow_label = NULL;
Label two_smis, call_operator;
if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, ECX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiBinaryOp);
overflow_label = deopt_blob->label();
__ movl(ECX, EAX); // Save if overflow (needs original value).
if (!left_info.IsClass(smi_class_) || !right_info.IsClass(smi_class_)) {
@@ -777,7 +789,8 @@
if (kind == Token::kBIT_AND) {
TraceOpt(node, kOptMessage);
Label is_smi, slow_case, done;
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EDX, kDeoptMintBinaryOp);
VisitLoadTwo(node->left(), node->right(), EAX, EDX);
__ testl(EDX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, &slow_case); // Call operator if right is not Smi.
@@ -899,7 +912,10 @@
Label* deopt_lbl = NULL;
if (!left_info.IsClass(double_class_) ||
!right_info.IsClass(double_class_)) {
- deopt_blob = AddDeoptimizationBlob(node, kLeftRegister, kRightRegister);
+ deopt_blob = AddDeoptimizationBlob(node,
+ kLeftRegister,
+ kRightRegister,
+ kDeoptDoubleBinaryOp);
deopt_lbl = deopt_blob->label();
}
@@ -1097,7 +1113,7 @@
const int int_value = (node->kind() == Token::kINCR) ? 1 : -1;
const Immediate smi_value =
Immediate(reinterpret_cast<int32_t>(Smi::New(int_value)));
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node);
+ DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, kDeoptIncrLocal);
__ testl(EAX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, deopt_blob->label());
__ addl(EAX, smi_value);
@@ -1160,7 +1176,8 @@
if (ic_data.NumberOfChecks() == 0) {
// Deoptimization point for this node is after receiver has been
// pushed twice on stack and before the getter (above) was executed.
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EBX, kDeoptIncrInstance);
__ jmp(deopt_blob->label());
return;
}
@@ -1178,7 +1195,8 @@
if (AtIdNodeHasOnlyClass(node, node->operator_id(), smi_class_)) {
// Deoptimization point for this node is after receiver has been
// pushed twice on stack and before the getter (above) was executed.
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EDX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EDX, EDX, kDeoptIncrInstanceOneClass);
if (return_original_value) {
// Preserve pre increment result.
__ movl(ECX, EAX);
@@ -1288,7 +1306,8 @@
// TODO(srdjan): Do not hardwire register.
UNIMPLEMENTED();
}
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EBX, kDeoptInstanceGetterSameTarget);
if (NodeMayBeSmi(receiver)) {
__ testl(EBX, Immediate(kSmiTagMask));
__ j(ZERO, deopt_blob->label());
@@ -1429,7 +1448,8 @@
if (ic_data.NumberOfChecks() == 0) {
// No type feedback collected.
node->receiver()->Visit(this);
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, kDeoptInstanceGetter);
__ jmp(deopt_blob->label());
return;
}
@@ -1456,8 +1476,8 @@
Register recv_reg,
Register value_reg) {
ASSERT((recv_reg != EBX) && (value_reg != EBX));
- DeoptimizationBlob* deopt_blob =
- AddDeoptimizationBlob(node, recv_reg, value_reg);
+ DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(
+ node, recv_reg, value_reg, kDeoptInstanceSetterSameTarget);
if (NodeMayBeSmi(receiver)) {
__ testl(recv_reg, Immediate(kSmiTagMask));
__ j(ZERO, deopt_blob->label());
@@ -1520,7 +1540,8 @@
VisitLoadTwo(node->receiver(), node->value(), EDX, EAX);
const ICData& ic_data = node->ICDataAtId(node->id());
if (ic_data.NumberOfChecks() == 0) {
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EDX, EAX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EDX, EAX, kDeoptInstanceSetter);
__ jmp(deopt_blob->label());
return;
}
@@ -1620,7 +1641,8 @@
Immediate(reinterpret_cast<intptr_t>(Object::null()));
Label evaluate_comparison;
if (!left_info.IsClass(smi_class_)) {
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiEquality);
Label left_not_null;
__ cmpl(EAX, raw_null);
__ j(NOT_EQUAL, &left_not_null, Assembler::kNearJump);
@@ -1712,13 +1734,15 @@
__ cmpl(EAX, EDX);
} else if (left_info.IsClass(smi_class_) || right_info.IsClass(smi_class_)) {
// One is Smi.
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EDX, kDeoptSmiCompareSmis);
Register reg_to_test = left_info.IsClass(smi_class_) ? EDX : EAX;
__ testl(reg_to_test, Immediate(kSmiTagMask));
__ j(NOT_ZERO, deopt_blob->label());
__ cmpl(EAX, EDX);
} else {
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, ECX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, ECX, EDX, kDeoptSmiCompareAny);
__ movl(ECX, EAX);
__ orl(EAX, EDX);
__ testl(EAX, Immediate(kSmiTagMask));
@@ -1812,15 +1836,18 @@
if (!CodeGenerator::IsResultNeeded(node)) {
return true;
}
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EDX);
Label compare;
// Comparison with NULL is "===".
const Immediate raw_null =
Immediate(reinterpret_cast<intptr_t>(Object::null()));
__ cmpl(EAX, raw_null);
if (num_classes == 0) {
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityNoFeedback);
__ j(NOT_EQUAL, deopt_blob->label());
} else {
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EDX, kDeoptEqualityClassCheck);
__ j(EQUAL, &compare);
// Smi causes deoptimization.
__ testl(EAX, Immediate(kSmiTagMask));
@@ -1876,7 +1903,7 @@
VisitLoadTwo(node->left(), node->right(), EAX, EDX);
DeoptimizationBlob* deopt_blob = NULL;
if (!left_info.IsClass(double_class_) || !right_info.IsClass(double_class_)) {
- deopt_blob = AddDeoptimizationBlob(node, EAX, EDX);
+ deopt_blob = AddDeoptimizationBlob(node, EAX, EDX, kDeoptDoubleComparison);
}
if (!left_info.IsClass(double_class_)) {
CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
@@ -1977,7 +2004,8 @@
if (AtIdNodeHasOnlyClass(node, node->id(), object_array_class) ||
AtIdNodeHasOnlyClass(node, node->id(), immutable_object_array_class)) {
VisitLoadTwo(node->array(), node->index_expr(), EBX, EDX);
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX, EDX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EBX, EDX, kDeoptLoadIndexedFixedArray);
const Class& test_class =
AtIdNodeHasOnlyClass(node, node->id(), object_array_class) ?
object_array_class : immutable_object_array_class;
@@ -2019,7 +2047,8 @@
intptr_t array_offset = GetFieldOffset(growable_array_class,
growable_array_array_field_name);
VisitLoadTwo(node->array(), node->index_expr(), EDX, EAX);
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EDX, EAX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EDX, EAX, kDeoptLoadIndexedGrowableArray);
// TODO(srdjan): Use CodeGenInfo to eliminate Smi test if possible.
// EAX: index, EDX: array.
__ testl(EAX, Immediate(kSmiTagMask));
@@ -2060,7 +2089,8 @@
Class::ZoneHandle(object_store->array_class());
if (AtIdNodeHasOnlyClass(node, node->id(), object_array_class)) {
VisitLoadTwo(node->index_expr(), node->value(), EBX, ECX);
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX, EBX, ECX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, EBX, ECX, kDeoptStoreIndexed);
__ popl(EAX); // array.
// ECX: value, EBX:index, EAX: array.
// Check type of array.
@@ -2214,7 +2244,7 @@
void OptimizingCodeGenerator::GenerateDirectCall(
intptr_t node_id,
intptr_t token_index,
- Function& target,
+ const Function& target,
intptr_t arg_count,
const Array& optional_argument_names) {
ASSERT(!target.IsNull());
@@ -2230,6 +2260,73 @@
}
+// Generate inline cache calls instead of deoptimizing when no type feedback is
+// provided.
+// TODO(srdjan): Recompilation framework should recognize active IC calls
+// in optimized code and mark them for reoptimization since type feedback was
+// collected in the meantime.
+void OptimizingCodeGenerator::GenerateInlineCacheCall(
+ intptr_t node_id,
+ intptr_t token_index,
+ const ICData& ic_data,
+ intptr_t num_args,
+ const Array& optional_arguments_names) {
+ __ LoadObject(ECX, Array::ZoneHandle(ic_data.data()));
+ __ LoadObject(EDX, ArgumentsDescriptor(num_args, optional_arguments_names));
+ ExternalLabel target_label(
+ "InlineCache", StubCode::InlineCacheEntryPoint());
+
+ __ call(&target_label);
+ AddCurrentDescriptor(PcDescriptors::kIcCall,
+ node_id,
+ token_index);
+ __ addl(ESP, Immediate(num_args * kWordSize));
+}
+
+
+// Normalizes the ic_data class/target pairs:
+// - If Smi class exists, make it the first one.
+// - If 'null_target' not null, append null-class/'null_target'
+void OptimizingCodeGenerator::NormalizeClassChecks(
+ const ICData& ic_data,
+ const Function& null_target,
+ GrowableArray<const Class*>* classes,
+ GrowableArray<const Function*>* targets) {
+ ASSERT(classes != NULL);
+ ASSERT(targets != NULL);
+ // Check if we can add Smi class in front.
+ Class& smi_test_class = Class::Handle();
+ Function& smi_target = Function::ZoneHandle();
+ for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
+ ic_data.GetOneClassCheckAt(i, &smi_test_class, &smi_target);
+ if (smi_test_class.raw() == smi_class_.raw()) {
+ classes->Add(&Class::ZoneHandle(smi_class_.raw()));
+ targets->Add(&Function::ZoneHandle(smi_target.raw()));
+ break;
+ }
+ }
+ // Add all classes except Smi.
+ for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
+ Function& target = Function::ZoneHandle();
+ Class& cls = Class::ZoneHandle();
+ ic_data.GetOneClassCheckAt(i, &cls, &target);
+ ASSERT(!cls.IsNullClass());
+ if (cls.raw() != smi_class_.raw()) {
+ ASSERT(!cls.IsNull());
+ ASSERT(!target.IsNull());
+ classes->Add(&cls);
+ targets->Add(&target);
+ }
+ }
+ // Do not add a target that has not been compiled yet.
+ if (!null_target.IsNull() && null_target.HasCode()) {
+ ASSERT(null_target.IsZoneHandle());
+ classes->Add(&Class::ZoneHandle(Object::null_class()));
+ targets->Add(&null_target);
+ }
+}
+
+
// Use ICData in 'node' to issues checks and calls.
void OptimizingCodeGenerator::GenerateCheckedInstanceCalls(
AstNode* node,
@@ -2237,84 +2334,88 @@
intptr_t node_id,
intptr_t token_index,
intptr_t num_args,
- const Array& optional_argument_names) {
+ const Array& optional_arguments_names) {
ASSERT(node != NULL);
ASSERT(receiver != NULL);
ASSERT(num_args > 0);
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node);
const ICData& ic_data = node->ICDataAtId(node_id);
if (ic_data.NumberOfChecks() == 0) {
- // No type feedback means node was never executed.
- __ jmp(deopt_blob->label());
+ // No type feedback means node was never executed. However that can be
+ // a common case especially in case of large switch statements.
+ // Use a special inline cache call which can help us decide when to
+ // re-optimize this optiumized function.
regis 2011/11/16 23:06:45 optiumized? opium-ized?
+ GenerateInlineCacheCall(
+ node_id, token_index, ic_data, num_args, optional_arguments_names);
return;
}
ASSERT(ic_data.NumberOfArgumentsChecked() == 1);
- // First test for Smi. Null object will cause deoptimization.
- intptr_t smi_class_index = -1;
- Class& smi_test_class = Class::Handle();
- Function& smi_target = Function::ZoneHandle();
- for (intptr_t i = 0; i < ic_data.NumberOfChecks(); i++) {
- ic_data.GetOneClassCheckAt(i, &smi_test_class, &smi_target);
- if (smi_test_class.raw() == smi_class_.raw()) {
- smi_class_index = i;
- break;
- }
- }
+ Function& target_for_null = Function::ZoneHandle();
+ ObjectStore* object_store = Isolate::Current()->object_store();
+ int num_optional_args =
+ optional_arguments_names.IsNull() ? 0 : optional_arguments_names.Length();
+ target_for_null = Resolver::ResolveDynamicForReceiverClass(
+ Class::Handle(object_store->object_class()),
+ String::Handle(ic_data.FunctionName()),
+ num_args,
+ num_optional_args);
+ GrowableArray<const Class*> classes;
+ GrowableArray<const Function*> targets;
+ NormalizeClassChecks(ic_data, target_for_null, &classes, &targets);
+ ASSERT(!classes.is_empty());
+ ASSERT(classes.length() == targets.length());
+ intptr_t start_ix = 0;
Label done;
__ movl(EAX, Address(ESP, (num_args - 1) * kWordSize)); // Load receiver.
- if (smi_class_index >= 0) {
+ if (classes[0]->raw() == smi_class_.raw()) {
+ start_ix++;
// Smi test is needed.
__ testl(EAX, Immediate(kSmiTagMask));
- if (ic_data.NumberOfChecks() == 1) {
- // Only the Smi test.
+ if (classes.length() == 1) {
+ // Only Smi test.
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, kDeoptCheckedInstanceCallSmiOnly);
__ j(NOT_ZERO, deopt_blob->label());
GenerateDirectCall(node_id,
token_index,
- smi_target,
+ *targets[0],
num_args,
- optional_argument_names);
+ optional_arguments_names);
return;
}
Label not_smi;
__ j(NOT_ZERO, &not_smi);
GenerateDirectCall(node_id,
token_index,
- smi_target,
+ *targets[0],
num_args,
- optional_argument_names);
+ optional_arguments_names);
__ jmp(&done);
__ Bind(&not_smi); // Continue with other test below.
} else if (NodeMayBeSmi(receiver)) {
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, kDeoptCheckedInstanceCallSmiFail);
__ testl(EAX, Immediate(kSmiTagMask));
__ j(ZERO, deopt_blob->label());
} else {
// Receiver cannot be Smi, no need to test it.
}
-
- intptr_t last_check_at = (smi_class_index == ic_data.NumberOfChecks() - 1) ?
- ic_data.NumberOfChecks() - 2 : ic_data.NumberOfChecks() - 1;
- // Every class may appear only once in the 'classes' array. Therefore, if
- // Smi class is last, it cannot be the second to last.
__ movl(EAX, FieldAddress(EAX, Object::class_offset())); // Receiver's class.
- for (intptr_t i = 0; i <= last_check_at; i++) {
- Function& target = Function::ZoneHandle();
- Class& cls = Class::ZoneHandle();
- ic_data.GetOneClassCheckAt(i, &cls, &target);
- ASSERT(!cls.IsNullClass());
- if (cls.raw() == smi_class_.raw()) {
- ASSERT(i < last_check_at); // Smi class may not be last.
- continue; // Skip Smi test.
- }
+ for (intptr_t i = start_ix; i < classes.length(); i++) {
+ const Class& cls = *classes[i];
+ const Function& target = *targets[i];
__ CompareObject(EAX, cls);
- if (i == last_check_at) {
+ if (i == (classes.length() - 1)) {
+ // Last check.
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, kDeoptCheckedInstanceCallCheckFail);
__ j(NOT_EQUAL, deopt_blob->label());
GenerateDirectCall(node_id,
token_index,
target,
num_args,
- optional_argument_names);
+ optional_arguments_names);
} else {
Label next;
__ j(NOT_EQUAL, &next);
@@ -2322,7 +2423,7 @@
token_index,
target,
num_args,
- optional_argument_names);
+ optional_arguments_names);
__ jmp(&done);
__ Bind(&next);
}
@@ -2382,7 +2483,8 @@
const ExternalLabel label(double_class_.ToCString(), stub.EntryPoint());
GenerateCall(node->token_index(), &label);
// EAX is double object.
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EBX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EBX, kDeoptIntegerToDouble);
__ popl(EBX); // Receiver
__ testl(EBX, Immediate(kSmiTagMask));
__ j(NOT_ZERO, deopt_blob->label()); // Deoptimize if not Smi.
@@ -2394,7 +2496,8 @@
if ((recognized == Recognizer::kDoubleToDouble) &&
AtIdNodeHasOnlyClass(node, node->id(), double_class_)) {
- DeoptimizationBlob* deopt_blob = AddDeoptimizationBlob(node, EAX);
+ DeoptimizationBlob* deopt_blob =
+ AddDeoptimizationBlob(node, EAX, kDeoptDoubleToDouble);
__ popl(EAX);
CheckIfDoubleOrSmi(EAX, EBX, deopt_blob->label(), deopt_blob->label());
return true;
« no previous file with comments | « runtime/vm/opt_code_generator_ia32.h ('k') | runtime/vm/stub_code_ia32.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698