ARM64: Fix and improve MacroAssembler::Printf.
authorJacob.Bramley@arm.com <Jacob.Bramley@arm.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 12 May 2014 15:44:21 +0000 (15:44 +0000)
committerJacob.Bramley@arm.com <Jacob.Bramley@arm.com@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 12 May 2014 15:44:21 +0000 (15:44 +0000)
  - W-sized values passed to Printf are now handled correctly by the
    simulator. In AAPCS64, int32_t and int64_t are passed in the same
    way, so this didn't affect non-simulator builds.
  - Since Printf now records the type and size of each argument, it is
    possible to mix argument types.
  - It is now possible to print the stack pointer. There is only one
    remaining restriction: The `csp` register cannot be printed unless
    it is the current stack pointer. This is because it is modified by
    BumpSystemStackPointer when the caller-saved registers are
    preserved.

BUG=
R=rmcilroy@chromium.org

Review URL: https://codereview.chromium.org/268353005

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21272 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/arm64/assembler-arm64-inl.h
src/arm64/assembler-arm64.h
src/arm64/instructions-arm64.h
src/arm64/macro-assembler-arm64.cc
src/arm64/macro-assembler-arm64.h
src/arm64/simulator-arm64.cc
src/arm64/simulator-arm64.h
test/cctest/test-assembler-arm64.cc

index 3c17153..70e3052 100644 (file)
@@ -109,8 +109,13 @@ inline bool CPURegister::IsNone() const {
 
 inline bool CPURegister::Is(const CPURegister& other) const {
   ASSERT(IsValidOrNone() && other.IsValidOrNone());
-  return (reg_code == other.reg_code) && (reg_size == other.reg_size) &&
-         (reg_type == other.reg_type);
+  return Aliases(other) && (reg_size == other.reg_size);
+}
+
+
+inline bool CPURegister::Aliases(const CPURegister& other) const {
+  ASSERT(IsValidOrNone() && other.IsValidOrNone());
+  return (reg_code == other.reg_code) && (reg_type == other.reg_type);
 }
 
 
@@ -195,16 +200,22 @@ inline void CPURegList::Remove(int code) {
 
 
 inline Register Register::XRegFromCode(unsigned code) {
-  // This function returns the zero register when code = 31. The stack pointer
-  // can not be returned.
-  ASSERT(code < kNumberOfRegisters);
-  return Register::Create(code, kXRegSizeInBits);
+  if (code == kSPRegInternalCode) {
+    return csp;
+  } else {
+    ASSERT(code < kNumberOfRegisters);
+    return Register::Create(code, kXRegSizeInBits);
+  }
 }
 
 
 inline Register Register::WRegFromCode(unsigned code) {
-  ASSERT(code < kNumberOfRegisters);
-  return Register::Create(code, kWRegSizeInBits);
+  if (code == kSPRegInternalCode) {
+    return wcsp;
+  } else {
+    ASSERT(code < kNumberOfRegisters);
+    return Register::Create(code, kWRegSizeInBits);
+  }
 }
 
 
index a3fbc98..5a28ea6 100644 (file)
@@ -66,6 +66,7 @@ struct CPURegister {
   bool IsValidFPRegister() const;
   bool IsNone() const;
   bool Is(const CPURegister& other) const;
+  bool Aliases(const CPURegister& other) const;
 
   bool IsZero() const;
   bool IsSP() const;
@@ -561,6 +562,11 @@ class CPURegList {
     return size_in_bits / kBitsPerByte;
   }
 
+  unsigned TotalSizeInBytes() const {
+    ASSERT(IsValid());
+    return RegisterSizeInBytes() * Count();
+  }
+
  private:
   RegList list_;
   unsigned size_;
index 968ddac..81ef6ad 100644 (file)
@@ -416,24 +416,38 @@ const Instr kImmExceptionIsUnreachable = 0xdebf;
 // A pseudo 'printf' instruction. The arguments will be passed to the platform
 // printf method.
 const Instr kImmExceptionIsPrintf = 0xdeb1;
-// Parameters are stored in ARM64 registers as if the printf pseudo-instruction
-// was a call to the real printf method:
-//
-// x0: The format string, then either of:
+// Most parameters are stored in ARM64 registers as if the printf
+// pseudo-instruction was a call to the real printf method:
+//      x0: The format string.
 //   x1-x7: Optional arguments.
 //   d0-d7: Optional arguments.
 //
-// Floating-point and integer arguments are passed in separate sets of
-// registers in AAPCS64 (even for varargs functions), so it is not possible to
-// determine the type of location of each arguments without some information
-// about the values that were passed in. This information could be retrieved
-// from the printf format string, but the format string is not trivial to
-// parse so we encode the relevant information with the HLT instruction.
-// - Type
-//    Either kRegister or kFPRegister, but stored as a uint32_t because there's
-//    no way to guarantee the size of the CPURegister::RegisterType enum.
-const unsigned kPrintfTypeOffset = 1 * kInstructionSize;
-const unsigned kPrintfLength = 2 * kInstructionSize;
+// Also, the argument layout is described inline in the instructions:
+//  - arg_count: The number of arguments.
+//  - arg_pattern: A set of PrintfArgPattern values, packed into two-bit fields.
+//
+// Floating-point and integer arguments are passed in separate sets of registers
+// in AAPCS64 (even for varargs functions), so it is not possible to determine
+// the type of each argument without some information about the values that were
+// passed in. This information could be retrieved from the printf format string,
+// but the format string is not trivial to parse so we encode the relevant
+// information with the HLT instruction.
+const unsigned kPrintfArgCountOffset = 1 * kInstructionSize;
+const unsigned kPrintfArgPatternListOffset = 2 * kInstructionSize;
+const unsigned kPrintfLength = 3 * kInstructionSize;
+
+const unsigned kPrintfMaxArgCount = 4;
+
+// The argument pattern is a set of two-bit-fields, each with one of the
+// following values:
+enum PrintfArgPattern {
+  kPrintfArgW = 1,
+  kPrintfArgX = 2,
+  // There is no kPrintfArgS because floats are always converted to doubles in C
+  // varargs calls.
+  kPrintfArgD = 3
+};
+static const unsigned kPrintfArgPatternBits = 2;
 
 // A pseudo 'debug' instruction.
 const Instr kImmExceptionIsDebug = 0xdeb0;
index 809821a..7279319 100644 (file)
@@ -4861,108 +4861,95 @@ void MacroAssembler::PrintfNoPreserve(const char * format,
   // in most cases anyway, so this restriction shouldn't be too serious.
   ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer()));
 
-  // Make sure that the macro assembler doesn't try to use any of our arguments
-  // as scratch registers.
-  ASSERT(!TmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3));
-  ASSERT(!FPTmpList()->IncludesAliasOf(arg0, arg1, arg2, arg3));
-
-  // We cannot print the stack pointer because it is typically used to preserve
-  // caller-saved registers (using other Printf variants which depend on this
-  // helper).
-  ASSERT(!AreAliased(arg0, StackPointer()));
-  ASSERT(!AreAliased(arg1, StackPointer()));
-  ASSERT(!AreAliased(arg2, StackPointer()));
-  ASSERT(!AreAliased(arg3, StackPointer()));
-
-  static const int kMaxArgCount = 4;
-  // Assume that we have the maximum number of arguments until we know
-  // otherwise.
-  int arg_count = kMaxArgCount;
-
-  // The provided arguments.
-  CPURegister args[kMaxArgCount] = {arg0, arg1, arg2, arg3};
-
-  // The PCS registers where the arguments need to end up.
-  CPURegister pcs[kMaxArgCount] = {NoCPUReg, NoCPUReg, NoCPUReg, NoCPUReg};
-
-  // Promote FP arguments to doubles, and integer arguments to X registers.
-  // Note that FP and integer arguments cannot be mixed, but we'll check
-  // AreSameSizeAndType once we've processed these promotions.
-  for (int i = 0; i < kMaxArgCount; i++) {
+  // The provided arguments, and their proper procedure-call standard registers.
+  CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3};
+  CPURegister pcs[kPrintfMaxArgCount] = {NoReg, NoReg, NoReg, NoReg};
+
+  int arg_count = kPrintfMaxArgCount;
+
+  // The PCS varargs registers for printf. Note that x0 is used for the printf
+  // format string.
+  static const CPURegList kPCSVarargs =
+      CPURegList(CPURegister::kRegister, kXRegSizeInBits, 1, arg_count);
+  static const CPURegList kPCSVarargsFP =
+      CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 0, arg_count - 1);
+
+  // We can use caller-saved registers as scratch values, except for the
+  // arguments and the PCS registers where they might need to go.
+  CPURegList tmp_list = kCallerSaved;
+  tmp_list.Remove(x0);      // Used to pass the format string.
+  tmp_list.Remove(kPCSVarargs);
+  tmp_list.Remove(arg0, arg1, arg2, arg3);
+
+  CPURegList fp_tmp_list = kCallerSavedFP;
+  fp_tmp_list.Remove(kPCSVarargsFP);
+  fp_tmp_list.Remove(arg0, arg1, arg2, arg3);
+
+  // Override the MacroAssembler's scratch register list. The lists will be
+  // reset automatically at the end of the UseScratchRegisterScope.
+  UseScratchRegisterScope temps(this);
+  TmpList()->set_list(tmp_list.list());
+  FPTmpList()->set_list(fp_tmp_list.list());
+
+  // Copies of the printf vararg registers that we can pop from.
+  CPURegList pcs_varargs = kPCSVarargs;
+  CPURegList pcs_varargs_fp = kPCSVarargsFP;
+
+  // Place the arguments. There are lots of clever tricks and optimizations we
+  // could use here, but Printf is a debug tool so instead we just try to keep
+  // it simple: Move each input that isn't already in the right place to a
+  // scratch register, then move everything back.
+  for (unsigned i = 0; i < kPrintfMaxArgCount; i++) {
+    // Work out the proper PCS register for this argument.
     if (args[i].IsRegister()) {
-      // Note that we use x1 onwards, because x0 will hold the format string.
-      pcs[i] = Register::XRegFromCode(i + 1);
-      // For simplicity, we handle all integer arguments as X registers. An X
-      // register argument takes the same space as a W register argument in the
-      // PCS anyway. The only limitation is that we must explicitly clear the
-      // top word for W register arguments as the callee will expect it to be
-      // clear.
-      if (!args[i].Is64Bits()) {
-        const Register& as_x = args[i].X();
-        And(as_x, as_x, 0x00000000ffffffff);
-        args[i] = as_x;
-      }
+      pcs[i] = pcs_varargs.PopLowestIndex().X();
+      // We might only need a W register here. We need to know the size of the
+      // argument so we can properly encode it for the simulator call.
+      if (args[i].Is32Bits()) pcs[i] = pcs[i].W();
     } else if (args[i].IsFPRegister()) {
-      pcs[i] = FPRegister::DRegFromCode(i);
-      // C and C++ varargs functions (such as printf) implicitly promote float
-      // arguments to doubles.
-      if (!args[i].Is64Bits()) {
-        FPRegister s(args[i]);
-        const FPRegister& as_d = args[i].D();
-        Fcvt(as_d, s);
-        args[i] = as_d;
-      }
+      // In C, floats are always cast to doubles for varargs calls.
+      pcs[i] = pcs_varargs_fp.PopLowestIndex().D();
     } else {
-      // This is the first empty (NoCPUReg) argument, so use it to set the
-      // argument count and bail out.
+      ASSERT(args[i].IsNone());
       arg_count = i;
       break;
     }
-  }
-  ASSERT((arg_count >= 0) && (arg_count <= kMaxArgCount));
-  // Check that every remaining argument is NoCPUReg.
-  for (int i = arg_count; i < kMaxArgCount; i++) {
-    ASSERT(args[i].IsNone());
-  }
-  ASSERT((arg_count == 0) || AreSameSizeAndType(args[0], args[1],
-                                                args[2], args[3],
-                                                pcs[0], pcs[1],
-                                                pcs[2], pcs[3]));
 
-  // Move the arguments into the appropriate PCS registers.
-  //
-  // Arranging an arbitrary list of registers into x1-x4 (or d0-d3) is
-  // surprisingly complicated.
-  //
-  //  * For even numbers of registers, we push the arguments and then pop them
-  //    into their final registers. This maintains 16-byte stack alignment in
-  //    case csp is the stack pointer, since we're only handling X or D
-  //    registers at this point.
-  //
-  //  * For odd numbers of registers, we push and pop all but one register in
-  //    the same way, but the left-over register is moved directly, since we
-  //    can always safely move one register without clobbering any source.
-  if (arg_count >= 4) {
-    Push(args[3], args[2], args[1], args[0]);
-  } else if (arg_count >= 2) {
-    Push(args[1], args[0]);
-  }
-
-  if ((arg_count % 2) != 0) {
-    // Move the left-over register directly.
-    const CPURegister& leftover_arg = args[arg_count - 1];
-    const CPURegister& leftover_pcs = pcs[arg_count - 1];
-    if (leftover_arg.IsRegister()) {
-      Mov(Register(leftover_pcs), Register(leftover_arg));
-    } else {
-      Fmov(FPRegister(leftover_pcs), FPRegister(leftover_arg));
+    // If the argument is already in the right place, leave it where it is.
+    if (args[i].Aliases(pcs[i])) continue;
+
+    // Otherwise, if the argument is in a PCS argument register, allocate an
+    // appropriate scratch register and then move it out of the way.
+    if (kPCSVarargs.IncludesAliasOf(args[i]) ||
+        kPCSVarargsFP.IncludesAliasOf(args[i])) {
+      if (args[i].IsRegister()) {
+        Register old_arg = Register(args[i]);
+        Register new_arg = temps.AcquireSameSizeAs(old_arg);
+        Mov(new_arg, old_arg);
+        args[i] = new_arg;
+      } else {
+        FPRegister old_arg = FPRegister(args[i]);
+        FPRegister new_arg = temps.AcquireSameSizeAs(old_arg);
+        Fmov(new_arg, old_arg);
+        args[i] = new_arg;
+      }
     }
   }
 
-  if (arg_count >= 4) {
-    Pop(pcs[0], pcs[1], pcs[2], pcs[3]);
-  } else if (arg_count >= 2) {
-    Pop(pcs[0], pcs[1]);
+  // Do a second pass to move values into their final positions and perform any
+  // conversions that may be required.
+  for (int i = 0; i < arg_count; i++) {
+    ASSERT(pcs[i].type() == args[i].type());
+    if (pcs[i].IsRegister()) {
+      Mov(Register(pcs[i]), Register(args[i]), kDiscardForSameWReg);
+    } else {
+      ASSERT(pcs[i].IsFPRegister());
+      if (pcs[i].SizeInBytes() == args[i].SizeInBytes()) {
+        Fmov(FPRegister(pcs[i]), FPRegister(args[i]));
+      } else {
+        Fcvt(FPRegister(pcs[i]), FPRegister(args[i]));
+      }
+    }
   }
 
   // Load the format string into x0, as per the procedure-call standard.
@@ -4990,18 +4977,33 @@ void MacroAssembler::PrintfNoPreserve(const char * format,
     Bic(csp, StackPointer(), 0xf);
   }
 
-  CallPrintf(pcs[0].type());
+  CallPrintf(arg_count, pcs);
 }
 
 
-void MacroAssembler::CallPrintf(CPURegister::RegisterType type) {
+void MacroAssembler::CallPrintf(int arg_count, const CPURegister * args) {
   // A call to printf needs special handling for the simulator, since the system
   // printf function will use a different instruction set and the procedure-call
   // standard will not be compatible.
 #ifdef USE_SIMULATOR
   { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize);
     hlt(kImmExceptionIsPrintf);
-    dc32(type);
+    dc32(arg_count);          // kPrintfArgCountOffset
+
+    // Determine the argument pattern.
+    uint32_t arg_pattern_list = 0;
+    for (int i = 0; i < arg_count; i++) {
+      uint32_t arg_pattern;
+      if (args[i].IsRegister()) {
+        arg_pattern = args[i].Is32Bits() ? kPrintfArgW : kPrintfArgX;
+      } else {
+        ASSERT(args[i].Is64Bits());
+        arg_pattern = kPrintfArgD;
+      }
+      ASSERT(arg_pattern < (1 << kPrintfArgPatternBits));
+      arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i));
+    }
+    dc32(arg_pattern_list);   // kPrintfArgPatternListOffset
   }
 #else
   Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE);
@@ -5010,10 +5012,18 @@ void MacroAssembler::CallPrintf(CPURegister::RegisterType type) {
 
 
 void MacroAssembler::Printf(const char * format,
-                            const CPURegister& arg0,
-                            const CPURegister& arg1,
-                            const CPURegister& arg2,
-                            const CPURegister& arg3) {
+                            CPURegister arg0,
+                            CPURegister arg1,
+                            CPURegister arg2,
+                            CPURegister arg3) {
+  // We can only print sp if it is the current stack pointer.
+  if (!csp.Is(StackPointer())) {
+    ASSERT(!csp.Aliases(arg0));
+    ASSERT(!csp.Aliases(arg1));
+    ASSERT(!csp.Aliases(arg2));
+    ASSERT(!csp.Aliases(arg3));
+  }
+
   // Printf is expected to preserve all registers, so make sure that none are
   // available as scratch registers until we've preserved them.
   RegList old_tmp_list = TmpList()->list();
@@ -5035,19 +5045,41 @@ void MacroAssembler::Printf(const char * format,
   TmpList()->set_list(tmp_list.list());
   FPTmpList()->set_list(fp_tmp_list.list());
 
-  // Preserve NZCV.
   { UseScratchRegisterScope temps(this);
-    Register tmp = temps.AcquireX();
-    Mrs(tmp, NZCV);
-    Push(tmp, xzr);
-  }
+    // If any of the arguments are the current stack pointer, allocate a new
+    // register for them, and adjust the value to compensate for pushing the
+    // caller-saved registers.
+    bool arg0_sp = StackPointer().Aliases(arg0);
+    bool arg1_sp = StackPointer().Aliases(arg1);
+    bool arg2_sp = StackPointer().Aliases(arg2);
+    bool arg3_sp = StackPointer().Aliases(arg3);
+    if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) {
+      // Allocate a register to hold the original stack pointer value, to pass
+      // to PrintfNoPreserve as an argument.
+      Register arg_sp = temps.AcquireX();
+      Add(arg_sp, StackPointer(),
+          kCallerSaved.TotalSizeInBytes() + kCallerSavedFP.TotalSizeInBytes());
+      if (arg0_sp) arg0 = Register::Create(arg_sp.code(), arg0.SizeInBits());
+      if (arg1_sp) arg1 = Register::Create(arg_sp.code(), arg1.SizeInBits());
+      if (arg2_sp) arg2 = Register::Create(arg_sp.code(), arg2.SizeInBits());
+      if (arg3_sp) arg3 = Register::Create(arg_sp.code(), arg3.SizeInBits());
+    }
 
-  PrintfNoPreserve(format, arg0, arg1, arg2, arg3);
+    // Preserve NZCV.
+    { UseScratchRegisterScope temps(this);
+      Register tmp = temps.AcquireX();
+      Mrs(tmp, NZCV);
+      Push(tmp, xzr);
+    }
 
-  { UseScratchRegisterScope temps(this);
-    Register tmp = temps.AcquireX();
-    Pop(xzr, tmp);
-    Msr(NZCV, tmp);
+    PrintfNoPreserve(format, arg0, arg1, arg2, arg3);
+
+    // Restore NZCV.
+    { UseScratchRegisterScope temps(this);
+      Register tmp = temps.AcquireX();
+      Pop(xzr, tmp);
+      Msr(NZCV, tmp);
+    }
   }
 
   PopCPURegList(kCallerSavedFP);
index 4ce240e..c9ebab3 100644 (file)
@@ -1930,12 +1930,13 @@ class MacroAssembler : public Assembler {
   // (such as %e, %f or %g) are FPRegisters, and that arguments for integer
   // placeholders are Registers.
   //
-  // A maximum of four arguments may be given to any single Printf call. The
-  // arguments must be of the same type, but they do not need to have the same
-  // size.
+  // At the moment it is only possible to print the value of csp if it is the
+  // current stack pointer. Otherwise, the MacroAssembler will automatically
+  // update csp on every push (using BumpSystemStackPointer), so determining its
+  // value is difficult.
   //
-  // The following registers cannot be printed:
-  //    StackPointer(), csp.
+  // Format placeholders that refer to more than one argument, or to a specific
+  // argument, are not supported. This includes formats like "%1$d" or "%.*d".
   //
   // This function automatically preserves caller-saved registers so that
   // calling code can use Printf at any point without having to worry about
@@ -1943,15 +1944,11 @@ class MacroAssembler : public Assembler {
   // a problem, preserve the important registers manually and then call
   // PrintfNoPreserve. Callee-saved registers are not used by Printf, and are
   // implicitly preserved.
-  //
-  // This function assumes (and asserts) that the current stack pointer is
-  // callee-saved, not caller-saved. This is most likely the case anyway, as a
-  // caller-saved stack pointer doesn't make a lot of sense.
   void Printf(const char * format,
-              const CPURegister& arg0 = NoCPUReg,
-              const CPURegister& arg1 = NoCPUReg,
-              const CPURegister& arg2 = NoCPUReg,
-              const CPURegister& arg3 = NoCPUReg);
+              CPURegister arg0 = NoCPUReg,
+              CPURegister arg1 = NoCPUReg,
+              CPURegister arg2 = NoCPUReg,
+              CPURegister arg3 = NoCPUReg);
 
   // Like Printf, but don't preserve any caller-saved registers, not even 'lr'.
   //
@@ -2046,8 +2043,10 @@ class MacroAssembler : public Assembler {
   // arguments and stack (csp) must be prepared by the caller as for a normal
   // AAPCS64 call to 'printf'.
   //
-  // The 'type' argument specifies the type of the optional arguments.
-  void CallPrintf(CPURegister::RegisterType type = CPURegister::kNoRegister);
+  // The 'args' argument should point to an array of variable arguments in their
+  // proper PCS registers (and in calling order). The argument registers can
+  // have mixed types. The format string (x0) should not be included.
+  void CallPrintf(int arg_count = 0, const CPURegister * args = NULL);
 
   // Helper for throwing exceptions.  Compute a handler address and jump to
   // it.  See the implementation for register usage.
index 3c970f8..c7c9f6c 100644 (file)
@@ -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
index 543385b..9dd353a 100644 (file)
@@ -757,6 +757,9 @@ class Simulator : public DecoderVisitor {
   void CorruptAllCallerSavedCPURegisters();
 #endif
 
+  // Pseudo Printf instruction
+  void DoPrintf(Instruction* instr);
+
   // Processor state ---------------------------------------
 
   // Output stream.
index c1310f0..c89ccce 100644 (file)
@@ -9780,7 +9780,7 @@ TEST(cpureglist_utils_empty) {
 
 TEST(printf) {
   INIT_V8();
-  SETUP();
+  SETUP_SIZE(BUF_SIZE * 2);
   START();
 
   char const * test_plain_string = "Printf with no arguments.\n";
@@ -9821,41 +9821,49 @@ TEST(printf) {
   __ Mov(x11, 40);
   __ Mov(x12, 500);
 
-  // x8 and x9 are used by debug code in part of the macro assembler. However,
-  // Printf guarantees to preserve them (so we can use Printf in debug code),
-  // and we need to test that they are properly preserved. The above code
-  // shouldn't need to use them, but we initialize x8 and x9 last to be on the
-  // safe side. This test still assumes that none of the code from
-  // before->Dump() to the end of the test can clobber x8 or x9, so where
-  // possible we use the Assembler directly to be safe.
-  __ orr(x8, xzr, 0x8888888888888888);
-  __ orr(x9, xzr, 0x9999999999999999);
-
-  // Check that we don't clobber any registers, except those that we explicitly
-  // write results into.
+  // A single character.
+  __ Mov(w13, 'x');
+
+  // Check that we don't clobber any registers.
   before.Dump(&masm);
 
   __ Printf(test_plain_string);   // NOLINT(runtime/printf)
-  __ Printf("x0: %" PRId64", x1: 0x%08" PRIx64 "\n", x0, x1);
+  __ Printf("x0: %" PRId64 ", x1: 0x%08" PRIx64 "\n", x0, x1);
+  __ Printf("w5: %" PRId32 ", x5: %" PRId64"\n", w5, x5);
   __ Printf("d0: %f\n", d0);
   __ Printf("Test %%s: %s\n", x2);
   __ Printf("w3(uint32): %" PRIu32 "\nw4(int32): %" PRId32 "\n"
             "x5(uint64): %" PRIu64 "\nx6(int64): %" PRId64 "\n",
             w3, w4, x5, x6);
   __ Printf("%%f: %f\n%%g: %g\n%%e: %e\n%%E: %E\n", s1, s2, d3, d4);
-  __ Printf("0x%08" PRIx32 ", 0x%016" PRIx64 "\n", x28, x28);
+  __ Printf("0x%" PRIx32 ", 0x%" PRIx64 "\n", w28, x28);
   __ Printf("%g\n", d10);
+  __ Printf("%%%%%s%%%c%%\n", x2, w13);
+
+  // Print the stack pointer (csp).
+  ASSERT(csp.Is(__ StackPointer()));
+  __ Printf("StackPointer(csp): 0x%016" PRIx64 ", 0x%08" PRIx32 "\n",
+            __ StackPointer(), __ StackPointer().W());
 
   // Test with a different stack pointer.
   const Register old_stack_pointer = __ StackPointer();
-  __ mov(x29, old_stack_pointer);
+  __ Mov(x29, old_stack_pointer);
   __ SetStackPointer(x29);
-  __ Printf("old_stack_pointer: 0x%016" PRIx64 "\n", old_stack_pointer);
-  __ mov(old_stack_pointer, __ StackPointer());
+  // Print the stack pointer (not csp).
+  __ Printf("StackPointer(not csp): 0x%016" PRIx64 ", 0x%08" PRIx32 "\n",
+            __ StackPointer(), __ StackPointer().W());
+  __ Mov(old_stack_pointer, __ StackPointer());
   __ SetStackPointer(old_stack_pointer);
 
+  // Test with three arguments.
   __ Printf("3=%u, 4=%u, 5=%u\n", x10, x11, x12);
 
+  // Mixed argument types.
+  __ Printf("w3: %" PRIu32 ", s1: %f, x5: %" PRIu64 ", d3: %f\n",
+            w3, s1, x5, d3);
+  __ Printf("s1: %f, d3: %f, w3: %" PRId32 ", x5: %" PRId64 "\n",
+            s1, d3, w3, x5);
+
   END();
   RUN();
 
@@ -9877,7 +9885,7 @@ TEST(printf_no_preserve) {
   char const * test_plain_string = "Printf with no arguments.\n";
   char const * test_substring = "'This is a substring.'";
 
-  __ PrintfNoPreserve(test_plain_string);   // NOLINT(runtime/printf)
+  __ PrintfNoPreserve(test_plain_string);
   __ Mov(x19, x0);
 
   // Test simple integer arguments.
@@ -9915,7 +9923,7 @@ TEST(printf_no_preserve) {
 
   // Test printing callee-saved registers.
   __ Mov(x28, 0x123456789abcdef);
-  __ PrintfNoPreserve("0x%08" PRIx32 ", 0x%016" PRIx64 "\n", x28, x28);
+  __ PrintfNoPreserve("0x%" PRIx32 ", 0x%" PRIx64 "\n", w28, x28);
   __ Mov(x25, x0);
 
   __ Fmov(d10, 42.0);
@@ -9926,11 +9934,11 @@ TEST(printf_no_preserve) {
   const Register old_stack_pointer = __ StackPointer();
   __ Mov(x29, old_stack_pointer);
   __ SetStackPointer(x29);
-
-  __ PrintfNoPreserve("old_stack_pointer: 0x%016" PRIx64 "\n",
-                      old_stack_pointer);
+  // Print the stack pointer (not csp).
+  __ PrintfNoPreserve(
+      "StackPointer(not csp): 0x%016" PRIx64 ", 0x%08" PRIx32 "\n",
+      __ StackPointer(), __ StackPointer().W());
   __ Mov(x27, x0);
-
   __ Mov(old_stack_pointer, __ StackPointer());
   __ SetStackPointer(old_stack_pointer);
 
@@ -9941,6 +9949,15 @@ TEST(printf_no_preserve) {
   __ PrintfNoPreserve("3=%u, 4=%u, 5=%u\n", x3, x4, x5);
   __ Mov(x28, x0);
 
+  // Mixed argument types.
+  __ Mov(w3, 0xffffffff);
+  __ Fmov(s1, 1.234);
+  __ Mov(x5, 0xffffffffffffffff);
+  __ Fmov(d3, 3.456);
+  __ PrintfNoPreserve("w3: %" PRIu32 ", s1: %f, x5: %" PRIu64 ", d3: %f\n",
+                      w3, s1, x5, d3);
+  __ Mov(x29, x0);
+
   END();
   RUN();
 
@@ -9965,16 +9982,18 @@ TEST(printf_no_preserve) {
   // %e: 3.456000e+00
   // %E: 4.567000E+00
   ASSERT_EQUAL_64(13 + 10 + 17 + 17, x24);
-  // 0x89abcdef, 0x0123456789abcdef
-  ASSERT_EQUAL_64(31, x25);
+  // 0x89abcdef, 0x123456789abcdef
+  ASSERT_EQUAL_64(30, x25);
   // 42
   ASSERT_EQUAL_64(3, x26);
-  // old_stack_pointer: 0x00007fb037ae2370
+  // StackPointer(not csp): 0x00007fb037ae2370, 0x37ae2370
   // Note: This is an example value, but the field width is fixed here so the
   // string length is still predictable.
-  ASSERT_EQUAL_64(38, x27);
+  ASSERT_EQUAL_64(54, x27);
   // 3=3, 4=40, 5=500
   ASSERT_EQUAL_64(17, x28);
+  // w3: 4294967295, s1: 1.234000, x5: 18446744073709551615, d3: 3.456000
+  ASSERT_EQUAL_64(69, x29);
 
   TEARDOWN();
 }