Index: src/arm64/simulator-arm64.cc |
diff --git a/src/arm64/simulator-arm64.cc b/src/arm64/simulator-arm64.cc |
index 3c970f854a07a6f2bb64ca186d074b7a4cce981c..c7c9f6cedd9105aef766f45f273dfcf8226d9fc1 100644 |
--- a/src/arm64/simulator-arm64.cc |
+++ b/src/arm64/simulator-arm64.cc |
@@ -3581,43 +3581,7 @@ void Simulator::VisitException(Instruction* instr) { |
} else if (instr->ImmException() == kImmExceptionIsRedirectedCall) { |
DoRuntimeCall(instr); |
} else if (instr->ImmException() == kImmExceptionIsPrintf) { |
- // Read the argument encoded inline in the instruction stream. |
- uint32_t type; |
- memcpy(&type, |
- pc_->InstructionAtOffset(kPrintfTypeOffset), |
- sizeof(type)); |
- |
- const char* format = reg<const char*>(0); |
- |
- // Pass all of the relevant PCS registers onto printf. It doesn't |
- // matter if we pass too many as the extra ones won't be read. |
- int result; |
- fputs(clr_printf, stream_); |
- if (type == CPURegister::kRegister) { |
- result = fprintf(stream_, format, |
- xreg(1), xreg(2), xreg(3), xreg(4), |
- xreg(5), xreg(6), xreg(7)); |
- } else if (type == CPURegister::kFPRegister) { |
- result = fprintf(stream_, format, |
- dreg(0), dreg(1), dreg(2), dreg(3), |
- dreg(4), dreg(5), dreg(6), dreg(7)); |
- } else { |
- ASSERT(type == CPURegister::kNoRegister); |
- result = fprintf(stream_, "%s", format); |
- } |
- fputs(clr_normal, stream_); |
- |
-#ifdef DEBUG |
- CorruptAllCallerSavedCPURegisters(); |
-#endif |
- |
- set_xreg(0, result); |
- |
- // The printf parameters are inlined in the code, so skip them. |
- set_pc(pc_->InstructionAtOffset(kPrintfLength)); |
- |
- // Set LR as if we'd just called a native printf function. |
- set_lr(pc()); |
+ DoPrintf(instr); |
} else if (instr->ImmException() == kImmExceptionIsUnreachable) { |
fprintf(stream_, "Hit UNREACHABLE marker at PC=%p.\n", |
@@ -3635,6 +3599,133 @@ void Simulator::VisitException(Instruction* instr) { |
} |
} |
+ |
+void Simulator::DoPrintf(Instruction* instr) { |
+ ASSERT((instr->Mask(ExceptionMask) == HLT) && |
+ (instr->ImmException() == kImmExceptionIsPrintf)); |
+ |
+ // Read the arguments encoded inline in the instruction stream. |
+ uint32_t arg_count; |
+ uint32_t arg_pattern_list; |
+ STATIC_ASSERT(sizeof(*instr) == 1); |
+ memcpy(&arg_count, |
+ instr + kPrintfArgCountOffset, |
+ sizeof(arg_count)); |
+ memcpy(&arg_pattern_list, |
+ instr + kPrintfArgPatternListOffset, |
+ sizeof(arg_pattern_list)); |
+ |
+ ASSERT(arg_count <= kPrintfMaxArgCount); |
+ ASSERT((arg_pattern_list >> (kPrintfArgPatternBits * arg_count)) == 0); |
+ |
+ // We need to call the host printf function with a set of arguments defined by |
+ // arg_pattern_list. Because we don't know the types and sizes of the |
+ // arguments, this is very difficult to do in a robust and portable way. To |
+ // work around the problem, we pick apart the format string, and print one |
+ // format placeholder at a time. |
+ |
+ // Allocate space for the format string. We take a copy, so we can modify it. |
+ // Leave enough space for one extra character per expected argument (plus the |
+ // '\0' termination). |
+ const char * format_base = reg<const char *>(0); |
+ ASSERT(format_base != NULL); |
+ size_t length = strlen(format_base) + 1; |
+ char * const format = new char[length + arg_count]; |
+ |
+ // A list of chunks, each with exactly one format placeholder. |
+ const char * chunks[kPrintfMaxArgCount]; |
+ |
+ // Copy the format string and search for format placeholders. |
+ uint32_t placeholder_count = 0; |
+ char * format_scratch = format; |
+ for (size_t i = 0; i < length; i++) { |
+ if (format_base[i] != '%') { |
+ *format_scratch++ = format_base[i]; |
+ } else { |
+ if (format_base[i + 1] == '%') { |
+ // Ignore explicit "%%" sequences. |
+ *format_scratch++ = format_base[i]; |
+ |
+ if (placeholder_count == 0) { |
+ // The first chunk is passed to printf using "%s", so we need to |
+ // unescape "%%" sequences in this chunk. (Just skip the next '%'.) |
+ i++; |
+ } else { |
+ // Otherwise, pass through "%%" unchanged. |
+ *format_scratch++ = format_base[++i]; |
+ } |
+ } else { |
+ CHECK(placeholder_count < arg_count); |
+ // Insert '\0' before placeholders, and store their locations. |
+ *format_scratch++ = '\0'; |
+ chunks[placeholder_count++] = format_scratch; |
+ *format_scratch++ = format_base[i]; |
+ } |
+ } |
+ } |
+ ASSERT(format_scratch <= (format + length + arg_count)); |
+ CHECK(placeholder_count == arg_count); |
+ |
+ // Finally, call printf with each chunk, passing the appropriate register |
+ // argument. Normally, printf returns the number of bytes transmitted, so we |
+ // can emulate a single printf call by adding the result from each chunk. If |
+ // any call returns a negative (error) value, though, just return that value. |
+ |
+ fprintf(stream_, "%s", clr_printf); |
+ |
+ // Because '\0' is inserted before each placeholder, the first string in |
+ // 'format' contains no format placeholders and should be printed literally. |
+ int result = fprintf(stream_, "%s", format); |
+ int pcs_r = 1; // Start at x1. x0 holds the format string. |
+ int pcs_f = 0; // Start at d0. |
+ if (result >= 0) { |
+ for (uint32_t i = 0; i < placeholder_count; i++) { |
+ int part_result = -1; |
+ |
+ uint32_t arg_pattern = arg_pattern_list >> (i * kPrintfArgPatternBits); |
+ arg_pattern &= (1 << kPrintfArgPatternBits) - 1; |
+ switch (arg_pattern) { |
+ case kPrintfArgW: |
+ part_result = fprintf(stream_, chunks[i], wreg(pcs_r++)); |
+ break; |
+ case kPrintfArgX: |
+ part_result = fprintf(stream_, chunks[i], xreg(pcs_r++)); |
+ break; |
+ case kPrintfArgD: |
+ part_result = fprintf(stream_, chunks[i], dreg(pcs_f++)); |
+ break; |
+ default: UNREACHABLE(); |
+ } |
+ |
+ if (part_result < 0) { |
+ // Handle error values. |
+ result = part_result; |
+ break; |
+ } |
+ |
+ result += part_result; |
+ } |
+ } |
+ |
+ fprintf(stream_, "%s", clr_normal); |
+ |
+#ifdef DEBUG |
+ CorruptAllCallerSavedCPURegisters(); |
+#endif |
+ |
+ // Printf returns its result in x0 (just like the C library's printf). |
+ set_xreg(0, result); |
+ |
+ // The printf parameters are inlined in the code, so skip them. |
+ set_pc(instr->InstructionAtOffset(kPrintfLength)); |
+ |
+ // Set LR as if we'd just called a native printf function. |
+ set_lr(pc()); |
+ |
+ delete[] format; |
+} |
+ |
+ |
#endif // USE_SIMULATOR |
} } // namespace v8::internal |